<?php
declare(strict_types=1);
namespace App\Services;

use App\Core\Database;
use PDO;
use RuntimeException;

final class AdminTrashService
{
    private const ENTITIES = [
        'client' => ['table'=>'clients','label'=>'Adhérent'],
        'family' => ['table'=>'familles','label'=>'Famille'],
        'invoice' => ['table'=>'cda_invoices','label'=>'Facture'],
        'slot' => ['table'=>'cda_professor_slots','label'=>'Créneau'],
    ];

    public static function archive(string $type,int $id,int $org,int $user,string $label,string $reason=''): void
    {
        $entity=self::entity($type);$pdo=Database::connection();$pdo->beginTransaction();
        try{
            $q=$pdo->prepare("SELECT * FROM {$entity['table']} WHERE id=? AND organisation_id=? AND deleted_at IS NULL FOR UPDATE");
            $q->execute([$id,$org]);$row=$q->fetch(PDO::FETCH_ASSOC);
            if(!$row)throw new RuntimeException('Élément introuvable ou déjà archivé.');
            if($type==='invoice'&&($row['status']??'draft')!=='draft')throw new RuntimeException('Une facture émise ne peut pas être archivée. Créez un avoir.');
            if($type==='slot'){
                $c=$pdo->prepare("SELECT COUNT(*) FROM cda_slot_enrollments WHERE slot_id=? AND statut='active' AND deleted_at IS NULL");
                $c->execute([$id]);if((int)$c->fetchColumn()>0)throw new RuntimeException('Ce créneau contient une affectation active et ne peut pas être archivé.');
            }
            $pdo->prepare("UPDATE {$entity['table']} SET deleted_at=NOW() WHERE id=? AND organisation_id=?")->execute([$id,$org]);
            $session=date('Y-m-d').' / '.substr(session_id()?:bin2hex(random_bytes(4)),0,12);
            $pdo->prepare("INSERT INTO cda_admin_trash(organisation_id,entity_type,entity_id,entity_label,archive_session,reason,snapshot_json,archived_by) VALUES(?,?,?,?,?,?,?,?)")
                ->execute([$org,$type,$id,$label?:($entity['label'].' #'.$id),$session,trim($reason)?:null,json_encode($row,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),$user?:null]);
            $pdo->commit();
        }catch(\Throwable $e){if($pdo->inTransaction())$pdo->rollBack();throw $e;}
    }

    public static function grouped(int $org): array
    {
        $q=Database::connection()->prepare("SELECT t.*,CONCAT(COALESCE(u.prenom,''),' ',COALESCE(u.nom,'')) archived_by_name FROM cda_admin_trash t LEFT JOIN utilisateurs u ON u.id=t.archived_by WHERE t.organisation_id=? AND t.restored_at IS NULL AND t.purged_at IS NULL ORDER BY t.archived_at DESC,t.id DESC");
        $q->execute([$org]);$groups=[];foreach($q->fetchAll(PDO::FETCH_ASSOC) as $row)$groups[$row['archive_session']][]=$row;return $groups;
    }

    public static function restore(int $trashId,int $org,int $user): void
    {
        $pdo=Database::connection();$pdo->beginTransaction();
        try{$q=$pdo->prepare("SELECT * FROM cda_admin_trash WHERE id=? AND organisation_id=? AND restored_at IS NULL AND purged_at IS NULL FOR UPDATE");$q->execute([$trashId,$org]);$row=$q->fetch(PDO::FETCH_ASSOC);if(!$row)throw new RuntimeException('Archive introuvable.');$entity=self::entity((string)$row['entity_type']);$pdo->prepare("UPDATE {$entity['table']} SET deleted_at=NULL WHERE id=? AND organisation_id=?")->execute([(int)$row['entity_id'],$org]);$pdo->prepare("UPDATE cda_admin_trash SET restored_at=NOW(),restored_by=? WHERE id=?")->execute([$user?:null,$trashId]);$pdo->commit();}catch(\Throwable$e){if($pdo->inTransaction())$pdo->rollBack();throw$e;}
    }

    public static function purge(int $trashId,int $org,int $user): void
    {
        $pdo=Database::connection();$pdo->beginTransaction();
        try{$q=$pdo->prepare("SELECT * FROM cda_admin_trash WHERE id=? AND organisation_id=? AND restored_at IS NULL AND purged_at IS NULL FOR UPDATE");$q->execute([$trashId,$org]);$row=$q->fetch(PDO::FETCH_ASSOC);if(!$row)throw new RuntimeException('Archive introuvable.');$entity=self::entity((string)$row['entity_type']);$pdo->prepare("DELETE FROM {$entity['table']} WHERE id=? AND organisation_id=? AND deleted_at IS NOT NULL")->execute([(int)$row['entity_id'],$org]);$pdo->prepare("UPDATE cda_admin_trash SET purged_at=NOW(),purged_by=? WHERE id=?")->execute([$user?:null,$trashId]);$pdo->commit();}catch(\Throwable$e){if($pdo->inTransaction())$pdo->rollBack();throw$e;}
    }

    private static function entity(string $type): array
    {
        if(!isset(self::ENTITIES[$type]))throw new RuntimeException('Type d’archive interdit.');return self::ENTITIES[$type];
    }
}
