<?php
declare(strict_types=1);
namespace App\Services;
use App\Core\Database;
final class ZeroTrustPolicyService
{
 public function dashboard():array{$pdo=Database::connection();return ['policies'=>$pdo->query("SELECT id,code,name,description,priority,effect,min_trust_score,require_mfa,max_session_minutes,allowed_hours_start,allowed_hours_end,module_pattern,role_pattern,enabled,simulation_only,updated_at FROM system_zero_trust_policies ORDER BY priority,id")->fetchAll()?:[],'decisions'=>$pdo->query("SELECT policy_code,decision,trust_score,reason,user_id,module,route,created_at FROM system_zero_trust_decisions ORDER BY created_at DESC LIMIT 50")->fetchAll()?:[]];}
 public function save(array $i):void{$code=preg_replace('/[^a-z0-9._-]+/','-',strtolower(trim((string)($i['code']??''))))?:'policy-'.date('YmdHis');$name=substr(trim((string)($i['name']??'Nouvelle politique')),0,190);$desc=substr(trim((string)($i['description']??'')),0,1000);$effect=in_array(($i['effect']??''),['allow','deny','challenge'],true)?$i['effect']:'challenge';$pdo=Database::connection();$sql="INSERT INTO system_zero_trust_policies(code,name,description,priority,effect,min_trust_score,require_mfa,max_session_minutes,allowed_hours_start,allowed_hours_end,module_pattern,role_pattern,enabled,simulation_only,created_at,updated_at) VALUES(:code,:name,:description,:priority,:effect,:score,:mfa,:minutes,:start,:end,:module,:role,:enabled,:simulation,NOW(),NOW()) ON DUPLICATE KEY UPDATE name=VALUES(name),description=VALUES(description),priority=VALUES(priority),effect=VALUES(effect),min_trust_score=VALUES(min_trust_score),require_mfa=VALUES(require_mfa),max_session_minutes=VALUES(max_session_minutes),allowed_hours_start=VALUES(allowed_hours_start),allowed_hours_end=VALUES(allowed_hours_end),module_pattern=VALUES(module_pattern),role_pattern=VALUES(role_pattern),enabled=VALUES(enabled),simulation_only=VALUES(simulation_only),updated_at=NOW()";$s=$pdo->prepare($sql);$s->execute(['code'=>$code,'name'=>$name,'description'=>$desc,'priority'=>max(1,min(999,(int)($i['priority']??100))),'effect'=>$effect,'score'=>max(0,min(100,(int)($i['min_trust_score']??50))),'mfa'=>isset($i['require_mfa'])?1:0,'minutes'=>max(5,min(1440,(int)($i['max_session_minutes']??120))),'start'=>$this->time($i['allowed_hours_start']??null),'end'=>$this->time($i['allowed_hours_end']??null),'module'=>substr(trim((string)($i['module_pattern']??'*')),0,120),'role'=>substr(trim((string)($i['role_pattern']??'*')),0,120),'enabled'=>isset($i['enabled'])?1:0,'simulation'=>isset($i['simulation_only'])?1:0]);}
 public function toggle(int $id):void{if($id<1)return;$s=Database::connection()->prepare('UPDATE system_zero_trust_policies SET enabled=IF(enabled=1,0,1),updated_at=NOW() WHERE id=:id');$s->execute(['id'=>$id]);}
 public function simulate(array $i):array{$score=max(0,min(100,(int)($i['trust_score']??50)));$module=substr(trim((string)($i['module']??'')),0,120);$role=substr(trim((string)($i['role']??'')),0,120);$hour=(int)date('G');$pdo=Database::connection();$rows=$pdo->query("SELECT * FROM system_zero_trust_policies WHERE enabled=1 ORDER BY priority,id")->fetchAll()?:[];$decision='allow';$reason='Aucune politique bloquante.';$matched='';foreach($rows as $p){if(!$this->match((string)$p['module_pattern'],$module)||!$this->match((string)$p['role_pattern'],$role))continue;$matched=(string)$p['code'];$outside=false;if($p['allowed_hours_start']!==null&&$p['allowed_hours_end']!==null){$start=(int)substr((string)$p['allowed_hours_start'],0,2);$end=(int)substr((string)$p['allowed_hours_end'],0,2);$outside=$start<=$end?($hour<$start||$hour>$end):($hour>$end&&$hour<$start);}if($score<(int)$p['min_trust_score']||$outside||(int)$p['require_mfa']===1&&!isset($i['mfa'])){$decision=(string)$p['effect'];$reason=$score<(int)$p['min_trust_score']?'Score de confiance insuffisant.':($outside?'Accès hors plage autorisée.':'MFA requise.');break;}}$s=$pdo->prepare('INSERT INTO system_zero_trust_decisions(policy_code,decision,trust_score,reason,user_id,module,route,context_json,created_at) VALUES(:policy,:decision,:score,:reason,NULL,:module,:route,:context,NOW())');$s->execute(['policy'=>$matched?:null,'decision'=>$decision,'score'=>$score,'reason'=>$reason,'module'=>$module,'route'=>substr((string)($i['route']??''),0,190),'context'=>json_encode(['role'=>$role,'mfa'=>isset($i['mfa'])],JSON_UNESCAPED_UNICODE)]);return compact('decision','reason','matched','score');}
 private function match(string $pattern,string $value):bool{return $pattern===''||$pattern==='*'||fnmatch($pattern,$value,FNM_CASEFOLD);}
 private function time(mixed $v):?string{$v=trim((string)$v);return preg_match('/^([01]\d|2[0-3]):[0-5]\d$/',$v)?$v.':00':null;}
}
