<?php
declare(strict_types=1);
namespace App\Services\PackageManager;
use App\Core\Database;
use PDO;
use RuntimeException;
final class PackageCatalogService
{
    public function catalog(array $filters=[]): array
    {
        $pdo=Database::connection();
        (new PackageRegistryService())->synchronize(null);
        $stmt=$pdo->prepare('SELECT r.*,COALESCE(p.is_favorite,0) is_favorite,COALESCE(p.is_critical,0) is_critical,COALESCE(p.is_watched,0) is_watched FROM system_package_registry r LEFT JOIN system_package_catalog_preferences p ON p.registry_id=r.id ORDER BY COALESCE(r.installed_at,r.last_operation_at,r.created_at) DESC,r.id DESC');
        $stmt->execute(); $rows=$stmt->fetchAll(PDO::FETCH_ASSOC)?:[];
        $q=mb_strtolower(trim((string)($filters['q']??''))); $category=strtoupper(trim((string)($filters['category']??''))); $status=strtoupper(trim((string)($filters['status']??''))); $flag=strtolower(trim((string)($filters['flag']??'')));
        $rows=array_values(array_filter($rows,static function(array $r)use($q,$category,$status,$flag):bool{
            if($q!==''){
                $hay=mb_strtolower(implode(' ',[(string)$r['package_name'],(string)$r['package_key'],(string)$r['publisher'],(string)$r['version'],(string)$r['description']]));
                if(!str_contains($hay,$q)) return false;
            }
            if($category!=='' && strtoupper((string)$r['category'])!==$category) return false;
            if($status!=='' && strtoupper((string)$r['package_status'])!==$status) return false;
            if($flag==='favorite' && (int)$r['is_favorite']!==1) return false;
            if($flag==='critical' && (int)$r['is_critical']!==1) return false;
            if($flag==='watched' && (int)$r['is_watched']!==1) return false;
            return true;
        }));
        return ['packages'=>$rows,'metrics'=>$this->metrics($rows),'categories'=>$this->distinct($rows,'category'),'statuses'=>$this->distinct($rows,'package_status'),'compare_options'=>$this->compareOptions($pdo),'filters'=>['q'=>(string)($filters['q']??''),'category'=>(string)($filters['category']??''),'status'=>(string)($filters['status']??''),'flag'=>(string)($filters['flag']??''),'view'=>in_array(($filters['view']??'cards'),['cards','table'],true)?$filters['view']:'cards']];
    }
    public function setPreference(int $registryId,string $flag,?int $userId): void
    {
        if($registryId<=0 || !in_array($flag,['favorite','critical','watched'],true)) throw new RuntimeException('Préférence invalide.');
        $pdo=Database::connection();
        $check=$pdo->prepare('SELECT id FROM system_package_registry WHERE id=:id'); $check->execute(['id'=>$registryId]); if(!$check->fetchColumn()) throw new RuntimeException('Package introuvable.');
        $column=['favorite'=>'is_favorite','critical'=>'is_critical','watched'=>'is_watched'][$flag];
        $read=$pdo->prepare('SELECT is_favorite,is_critical,is_watched FROM system_package_catalog_preferences WHERE registry_id=:id'); $read->execute(['id'=>$registryId]); $current=$read->fetch(PDO::FETCH_ASSOC)?:['is_favorite'=>0,'is_critical'=>0,'is_watched'=>0];
        $current[$column]=(int)$current[$column]===1?0:1;
        $save=$pdo->prepare('INSERT INTO system_package_catalog_preferences(registry_id,is_favorite,is_critical,is_watched,updated_by,created_at,updated_at) VALUES(:id,:favorite,:critical,:watched,:user,NOW(),NOW()) ON DUPLICATE KEY UPDATE is_favorite=VALUES(is_favorite),is_critical=VALUES(is_critical),is_watched=VALUES(is_watched),updated_by=VALUES(updated_by),updated_at=NOW()');
        $save->execute(['id'=>$registryId,'favorite'=>$current['is_favorite'],'critical'=>$current['is_critical'],'watched'=>$current['is_watched'],'user'=>$userId]);
    }
    public function compare(int $leftId,int $rightId): array
    {
        if($leftId<=0 || $rightId<=0 || $leftId===$rightId) throw new RuntimeException('Sélectionnez deux packages différents.');
        $pdo=Database::connection(); $stmt=$pdo->prepare('SELECT * FROM system_package_registry WHERE id IN (:left_id,:right_id) ORDER BY id'); $stmt->execute(['left_id'=>$leftId,'right_id'=>$rightId]); $rows=$stmt->fetchAll(PDO::FETCH_ASSOC)?:[];
        $by=[]; foreach($rows as $r)$by[(int)$r['id']]=$r; if(!isset($by[$leftId],$by[$rightId])) throw new RuntimeException('Package de comparaison introuvable.');
        $fields=['version'=>'Version','category'=>'Catégorie','package_status'=>'État','publisher'=>'Éditeur','trust_score'=>'Trust','compatibility_score'=>'Compatibilité','certification_status'=>'Certification','policy_decision'=>'Politique','file_size'=>'Taille'];
        $diff=[]; foreach($fields as $key=>$label)$diff[]=['label'=>$label,'left'=>$by[$leftId][$key]??null,'right'=>$by[$rightId][$key]??null,'changed'=>(string)($by[$leftId][$key]??'')!==(string)($by[$rightId][$key]??'')];
        return ['left'=>$by[$leftId],'right'=>$by[$rightId],'differences'=>$diff];
    }
    private function metrics(array $rows): array
    {
        $m=['total'=>count($rows),'installed'=>0,'system'=>0,'obsolete'=>0,'repair'=>0,'favorites'=>0,'critical'=>0,'watched'=>0,'trust_average'=>0,'compatibility_average'=>0]; $t=[];$c=[];
        foreach($rows as $r){$s=strtoupper((string)$r['package_status']); if(in_array($s,['INSTALLED','SYSTEM'],true))$m['installed']++; if($s==='SYSTEM')$m['system']++; if($s==='OBSOLETE')$m['obsolete']++; if(in_array($s,['BROKEN','REPAIR_REQUIRED'],true))$m['repair']++; $m['favorites']+=(int)$r['is_favorite'];$m['critical']+=(int)$r['is_critical'];$m['watched']+=(int)$r['is_watched']; if($r['trust_score']!==null)$t[]=(int)$r['trust_score']; if($r['compatibility_score']!==null)$c[]=(int)$r['compatibility_score'];}
        $m['trust_average']=$t?(int)round(array_sum($t)/count($t)):0; $m['compatibility_average']=$c?(int)round(array_sum($c)/count($c)):0; return $m;
    }
    private function distinct(array $rows,string $key): array { $v=array_values(array_unique(array_map(static fn(array $r):string=>(string)$r[$key],$rows))); sort($v); return $v; }
    private function compareOptions(PDO $pdo): array { $s=$pdo->prepare('SELECT id,package_name,version FROM system_package_registry ORDER BY package_name,version DESC');$s->execute();return $s->fetchAll(PDO::FETCH_ASSOC)?:[]; }
}
