<?php
declare(strict_types=1);
namespace App\Reference;
use App\Core\Database;
use App\Reference\Contracts\ReferenceRepositoryInterface;
use PDO;
final class ReferenceRepository implements ReferenceRepositoryInterface
{
    private PDO $pdo;
    public function __construct(){ $this->pdo = Database::connection(); }
    public function types(bool $includeArchived=false): array {
        $org=$this->organisationId();
        $sql="SELECT t.*, CASE t.code WHEN 'tarifs' THEN (SELECT COUNT(*) FROM cda_prestation_catalog c WHERE c.organisation_id=".$org." AND c.actif=1) WHEN 'reductions' THEN (SELECT COUNT(*) FROM regles_reduction r WHERE r.organisation_id=".$org." AND r.actif=1) ELSE (SELECT COUNT(*) FROM axiom_reference_values v WHERE v.type_id=t.id AND v.archived_at IS NULL) END value_count FROM axiom_reference_types t";
        if(!$includeArchived){$sql.=' WHERE t.archived_at IS NULL';}
        $sql.=' ORDER BY t.display_order,t.label';
        return $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    }
    public function typeByCode(string $code): ?array { $s=$this->pdo->prepare('SELECT * FROM axiom_reference_types WHERE code=:code LIMIT 1');$s->execute(['code'=>$code]);$r=$s->fetch(PDO::FETCH_ASSOC);return $r?:null; }
    public function values(int $typeId,bool $includeArchived=false): array { $sql='SELECT * FROM axiom_reference_values WHERE type_id=:id'.($includeArchived?'':' AND archived_at IS NULL').' ORDER BY display_order,label';$s=$this->pdo->prepare($sql);$s->execute(['id'=>$typeId]);return $s->fetchAll(PDO::FETCH_ASSOC); }
    public function value(int $id): ?array { $s=$this->pdo->prepare('SELECT * FROM axiom_reference_values WHERE id=:id');$s->execute(['id'=>$id]);$r=$s->fetch(PDO::FETCH_ASSOC);return $r?:null; }
    public function synchronizeDisciplineDuration(int $referenceValueId,int $individual,int $collective): void {
        $individual=max(5,min(480,$individual));$collective=max(5,min(480,$collective));
        $value=$this->value($referenceValueId);if(!$value)throw new \RuntimeException('Discipline introuvable.');
        $meta=json_decode((string)($value['metadata']??''),true)?:[];
        $meta['duree_individuelle_minutes']=$individual;$meta['duree_collective_minutes']=$collective;
        $this->pdo->prepare('UPDATE axiom_reference_values SET metadata=?,updated_at=NOW() WHERE id=?')->execute([json_encode($meta,JSON_UNESCAPED_UNICODE),$referenceValueId]);
        $q=$this->pdo->prepare('UPDATE disciplines SET duree_individuelle_minutes=?,duree_collective_minutes=? WHERE organisation_id=? AND (LOWER(code)=LOWER(?) OR LOWER(libelle)=LOWER(?)) AND deleted_at IS NULL');
        $q->execute([$individual,$collective,$this->organisationId(),(string)$value['code'],(string)$value['label']]);
    }
    public function createType(array $d): int { $s=$this->pdo->prepare('INSERT INTO axiom_reference_types(code,label,description,icon,display_order,is_active,fields_schema,created_at,updated_at) VALUES(:code,:label,:description,:icon,:display_order,:is_active,:fields_schema,NOW(),NOW())');$s->execute($d);$id=(int)$this->pdo->lastInsertId();$this->log('type',$id,'create',null,$d);return $id; }
    public function updateType(int $id,array $d): void { $before=$this->typeRow($id);$d['id']=$id;$s=$this->pdo->prepare('UPDATE axiom_reference_types SET code=:code,label=:label,description=:description,icon=:icon,display_order=:display_order,is_active=:is_active,fields_schema=:fields_schema,updated_at=NOW() WHERE id=:id');$s->execute($d);$this->log('type',$id,'update',$before,$d); }
    public function archiveType(int $id): void { $before=$this->typeRow($id);$this->pdo->prepare('UPDATE axiom_reference_types SET archived_at=NOW(),is_active=0,updated_at=NOW() WHERE id=:id')->execute(['id'=>$id]);$this->log('type',$id,'archive',$before,null); }
    public function createValue(int $typeId,array $d): int {
        $d['type_id']=$typeId;
        $s=$this->pdo->prepare('INSERT INTO axiom_reference_values(type_id,code,label,description,icon,color,display_order,is_active,metadata,created_at,updated_at) VALUES(:type_id,:code,:label,:description,:icon,:color,:display_order,:is_active,:metadata,NOW(),NOW())');
        $s->execute($d);
        $id=(int)$this->pdo->lastInsertId();
        if($this->isContractType($typeId)){$this->synchronizeContractType(null,$d);}
        if($this->isRoomType($typeId)){$this->synchronizeRoom(null,$d);}
        $this->log('value',$id,'create',null,$d);
        return $id;
    }
    public function updateValue(int $id,array $d): void {
        $before=$this->value($id);$d['id']=$id;
        $s=$this->pdo->prepare('UPDATE axiom_reference_values SET code=:code,label=:label,description=:description,icon=:icon,color=:color,display_order=:display_order,is_active=:is_active,metadata=:metadata,updated_at=NOW() WHERE id=:id');
        $s->execute($d);
        if($before&&$this->isContractType((int)$before['type_id'])){$this->synchronizeContractType((string)$before['code'],$d);}
        if($before&&$this->isRoomType((int)$before['type_id'])){$this->synchronizeRoom((string)$before['code'],$d);}
        $this->log('value',$id,'update',$before,$d);
    }
    public function archiveValue(int $id): void {
        $before=$this->value($id);
        $this->pdo->prepare('UPDATE axiom_reference_values SET archived_at=NOW(),is_active=0,updated_at=NOW() WHERE id=:id')->execute(['id'=>$id]);
        if($before&&$this->isContractType((int)$before['type_id'])){$this->setContractTypeActive((string)$before['code'],false);}
        if($before&&$this->isRoomType((int)$before['type_id'])){$this->setRoomActive((string)$before['code'],false);}
        $this->log('value',$id,'archive',$before,null);
    }
    public function restoreValue(int $id): void {
        $before=$this->value($id);
        $this->pdo->prepare('UPDATE axiom_reference_values SET archived_at=NULL,is_active=1,updated_at=NOW() WHERE id=:id')->execute(['id'=>$id]);
        if($before&&$this->isContractType((int)$before['type_id'])){$this->setContractTypeActive((string)$before['code'],true);}
        if($before&&$this->isRoomType((int)$before['type_id'])){$this->setRoomActive((string)$before['code'],true);}
        $this->log('value',$id,'restore',$before,null);
    }
    public function history(int $limit=50): array { $limit=max(1,min(200,$limit));return $this->pdo->query('SELECT * FROM axiom_reference_history ORDER BY id DESC LIMIT '.$limit)->fetchAll(PDO::FETCH_ASSOC); }
    public function stats(): array { $org=$this->organisationId();$catalog=(int)$this->pdo->query('SELECT COUNT(*) FROM cda_prestation_catalog WHERE organisation_id='.$org.' AND actif=1')->fetchColumn();$discounts=(int)$this->pdo->query('SELECT COUNT(*) FROM regles_reduction WHERE organisation_id='.$org.' AND actif=1')->fetchColumn();return ['types'=>(int)$this->pdo->query('SELECT COUNT(*) FROM axiom_reference_types WHERE archived_at IS NULL')->fetchColumn(),'values'=>(int)$this->pdo->query('SELECT COUNT(*) FROM axiom_reference_values WHERE archived_at IS NULL')->fetchColumn()+$catalog+$discounts,'archived'=>(int)$this->pdo->query('SELECT COUNT(*) FROM axiom_reference_values WHERE archived_at IS NOT NULL')->fetchColumn(),'active'=>(int)$this->pdo->query('SELECT COUNT(*) FROM axiom_reference_values WHERE archived_at IS NULL AND is_active=1')->fetchColumn()+$catalog+$discounts]; }
    private function typeRow(int $id): ?array {$s=$this->pdo->prepare('SELECT * FROM axiom_reference_types WHERE id=:id');$s->execute(['id'=>$id]);$r=$s->fetch(PDO::FETCH_ASSOC);return $r?:null;}
    private function isContractType(int $typeId): bool {
        $type=$this->typeRow($typeId);
        return ($type['code']??'')==='types_contrats';
    }
    private function isRoomType(int $typeId): bool {
        $type=$this->typeRow($typeId);
        return ($type['code']??'')==='salles';
    }
    private function organisationId(): int { return (int)($_SESSION['user']['organisation_id']??1); }
    private function synchronizeContractType(?string $oldCode,array $data): void {
        $metadata=json_decode((string)($data['metadata']??''),true)?:[];
        $code=strtoupper((string)$data['code']);
        $lookup=strtoupper($oldCode?:$code);
        $params=[
            'organisation_id'=>$this->organisationId(),'code'=>$code,
            'libelle'=>(string)$data['label'],'description'=>$data['description']?:null,
            'compte_anciennete'=>(int)($metadata['compte_anciennete']??1),
            'autorise_avenants'=>(int)($metadata['autorise_avenants']??1),
            'autorise_vacances'=>(int)($metadata['autorise_vacances']??1),
            'couleur'=>$data['color']?:'#64748B','icone'=>$data['icon']?:'📄',
            'ordre_affichage'=>(int)$data['display_order'],'actif'=>(int)$data['is_active'],
            'lookup'=>$lookup,
        ];
        $exists=$this->pdo->prepare('SELECT id FROM types_contrats_professeurs WHERE organisation_id=:organisation_id AND UPPER(code)=:lookup LIMIT 1');
        $exists->execute(['organisation_id'=>$params['organisation_id'],'lookup'=>$lookup]);
        $id=(int)$exists->fetchColumn();
        if($id){
            $params['id']=$id;unset($params['organisation_id'],$params['lookup']);
            $this->pdo->prepare('UPDATE types_contrats_professeurs SET code=:code,libelle=:libelle,description=:description,compte_anciennete=:compte_anciennete,autorise_avenants=:autorise_avenants,autorise_vacances=:autorise_vacances,couleur=:couleur,icone=:icone,ordre_affichage=:ordre_affichage,actif=:actif,deleted_at=NULL,updated_at=NOW() WHERE id=:id')->execute($params);
            return;
        }
        unset($params['lookup']);
        $this->pdo->prepare('INSERT INTO types_contrats_professeurs(organisation_id,code,libelle,description,compte_anciennete,autorise_avenants,autorise_vacances,couleur,icone,ordre_affichage,actif) VALUES(:organisation_id,:code,:libelle,:description,:compte_anciennete,:autorise_avenants,:autorise_vacances,:couleur,:icone,:ordre_affichage,:actif)')->execute($params);
    }
    private function setContractTypeActive(string $code,bool $active): void {
        $s=$this->pdo->prepare('UPDATE types_contrats_professeurs SET actif=:actif,deleted_at='.($active?'NULL':'NOW()').',updated_at=NOW() WHERE organisation_id=:organisation_id AND UPPER(code)=:code');
        $s->execute(['actif'=>$active?1:0,'organisation_id'=>$this->organisationId(),'code'=>strtoupper($code)]);
    }
    private function synchronizeRoom(?string $oldCode,array $data): void {
        $organisationId=$this->organisationId();
        $code=(string)$data['code'];
        $lookup=$oldCode?:$code;
        $find=$this->pdo->prepare('SELECT id FROM salles WHERE organisation_id=? AND LOWER(code)=LOWER(?) LIMIT 1');
        $find->execute([$organisationId,$lookup]);
        $id=(int)$find->fetchColumn();
        if($id){
            $this->pdo->prepare('UPDATE salles SET code=?,libelle=?,actif=? WHERE id=? AND organisation_id=?')
                ->execute([$code,(string)$data['label'],(int)$data['is_active'],$id,$organisationId]);
            return;
        }
        $this->pdo->prepare('INSERT INTO salles(organisation_id,code,libelle,capacite,actif) VALUES(?,?,?,NULL,?)')
            ->execute([$organisationId,$code,(string)$data['label'],(int)$data['is_active']]);
    }
    private function setRoomActive(string $code,bool $active): void {
        $this->pdo->prepare('UPDATE salles SET actif=? WHERE organisation_id=? AND LOWER(code)=LOWER(?)')
            ->execute([$active?1:0,$this->organisationId(),$code]);
    }
    private function log(string $kind,int $id,string $action,?array $before,?array $after): void { $uid=(int)($_SESSION['user']['id']??0);$s=$this->pdo->prepare('INSERT INTO axiom_reference_history(entity_kind,entity_id,action,before_data,after_data,user_id,created_at) VALUES(:kind,:id,:action,:before,:after,:user,NOW())');$s->execute(['kind'=>$kind,'id'=>$id,'action'=>$action,'before'=>$before?json_encode($before,JSON_UNESCAPED_UNICODE):null,'after'=>$after?json_encode($after,JSON_UNESCAPED_UNICODE):null,'user'=>$uid?:null]); }
}
