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

use App\Core\Database;
use InvalidArgumentException;

final class ThreatIntelligenceService
{
    /** @return array<string,mixed> */
    public function dashboard(array $filters=[]): array
    {
        $pdo=Database::connection();
        $type=$this->allowed((string)($filters['type']??''),['ip','domain','url','hash','email']);
        $status=$this->allowed((string)($filters['status']??''),['active','inactive','expired']);
        $sql='SELECT id,indicator_type,indicator_value,confidence,severity,source_name,status,first_seen_at,last_seen_at,expires_at,match_count,created_at FROM system_threat_indicators WHERE 1=1';
        $params=[];
        if($type!==''){$sql.=' AND indicator_type=:type';$params['type']=$type;}
        if($status!==''){$sql.=' AND status=:status';$params['status']=$status;}
        $sql.=' ORDER BY severity DESC,confidence DESC,last_seen_at DESC,id DESC LIMIT 250';
        $stmt=$pdo->prepare($sql);$stmt->execute($params);
        $detections=$pdo->prepare('SELECT d.id,d.rule_code,d.rule_name,d.entity_type,d.entity_id,d.score,d.severity,d.status,d.detected_at,d.details_json,i.title incident_title FROM system_security_detections d LEFT JOIN system_security_incidents i ON i.id=d.incident_id ORDER BY d.detected_at DESC,d.id DESC LIMIT 150');
        $detections->execute();
        $stats=$pdo->prepare("SELECT COUNT(*) total,SUM(status='active') active,SUM(severity='critical') critical,SUM(match_count) matches FROM system_threat_indicators");$stats->execute();
        $rules=$pdo->prepare('SELECT id,code,name,description,severity,score_weight,is_active,last_run_at,last_match_count FROM system_detection_rules ORDER BY name');$rules->execute();
        return ['indicators'=>$stmt->fetchAll()?:[],'detections'=>$detections->fetchAll()?:[],'stats'=>$stats->fetch()?:[],'rules'=>$rules->fetchAll()?:[]];
    }

    public function addIndicator(array $input,int $actorId): void
    {
        $type=$this->allowed((string)($input['indicator_type']??''),['ip','domain','url','hash','email']);
        $value=mb_substr(trim((string)($input['indicator_value']??'')),0,500);
        $severity=$this->allowed((string)($input['severity']??''),['low','medium','high','critical']);
        $confidence=max(0,min(100,(int)($input['confidence']??50)));
        $source=mb_substr(trim((string)($input['source_name']??'Interne')),0,160);
        if($type===''||$value===''||$severity==='')throw new InvalidArgumentException('Indicateur incomplet.');
        $pdo=Database::connection();
        $stmt=$pdo->prepare("INSERT INTO system_threat_indicators (indicator_type,indicator_value,indicator_hash,confidence,severity,source_name,status,first_seen_at,last_seen_at,created_by,created_at,updated_at) VALUES (:type,:value,SHA2(LOWER(:hash_value),256),:confidence,:severity,:source,'active',NOW(),NOW(),:actor,NOW(),NOW()) ON DUPLICATE KEY UPDATE confidence=GREATEST(confidence,VALUES(confidence)),severity=VALUES(severity),source_name=VALUES(source_name),status='active',last_seen_at=NOW(),updated_at=NOW()");
        $stmt->execute(['type'=>$type,'value'=>$value,'hash_value'=>$value,'confidence'=>$confidence,'severity'=>$severity,'source'=>$source!==''?$source:'Interne','actor'=>$actorId>0?$actorId:null]);
    }

    /** @return array<string,int> */
    public function runDetection(int $actorId): array
    {
        $pdo = Database::connection();
        $pdo->beginTransaction();
        $created = 0;
        $matched = 0;

        try {
            $rules = $pdo->prepare(
                'SELECT id,code,name,severity,score_weight,window_minutes,threshold_count
                 FROM system_detection_rules
                 WHERE is_active=1
                 ORDER BY id'
            );
            $rules->execute();

            foreach ($rules->fetchAll() ?: [] as $rule) {
                $minutes = max(1, min(10080, (int) $rule['window_minutes']));
                $threshold = max(1, (int) $rule['threshold_count']);
                $since = (new \DateTimeImmutable('now'))
                    ->modify('-' . $minutes . ' minutes')
                    ->format('Y-m-d H:i:s');

                if ($rule['code'] === 'AUTH_FAILURE_BURST') {
                    $q = $pdo->prepare(
                        "SELECT COALESCE(user_id,0) AS entity_id, COUNT(*) AS cnt
                         FROM system_security_events
                         WHERE occurred_at >= :since
                           AND (
                               action IN ('login_failed','auth_failed','access_denied')
                               OR result IN ('failed','denied','blocked','refused')
                               OR category IN ('authentication_failure','access_denied')
                           )
                         GROUP BY COALESCE(user_id,0)
                         HAVING COUNT(*) >= :threshold"
                    );
                } elseif ($rule['code'] === 'OFF_HOURS_ACTIVITY') {
                    $q = $pdo->prepare(
                        "SELECT COALESCE(user_id,0) AS entity_id, COUNT(*) AS cnt
                         FROM system_security_events
                         WHERE occurred_at >= :since
                           AND (HOUR(occurred_at) < 6 OR HOUR(occurred_at) >= 22)
                         GROUP BY COALESCE(user_id,0)
                         HAVING COUNT(*) >= :threshold"
                    );
                } else {
                    $q = $pdo->prepare(
                        "SELECT COALESCE(user_id,0) AS entity_id, COUNT(*) AS cnt
                         FROM system_security_events
                         WHERE occurred_at >= :since
                         GROUP BY COALESCE(user_id,0)
                         HAVING COUNT(*) >= :threshold"
                    );
                }

                $q->bindValue(':since', $since, \PDO::PARAM_STR);
                $q->bindValue(':threshold', $threshold, \PDO::PARAM_INT);
                $q->execute();

                $count = 0;
                foreach ($q->fetchAll() ?: [] as $row) {
                    $fingerprint = hash(
                        'sha256',
                        $rule['code'] . '|' . $row['entity_id'] . '|' . date('Y-m-d-H')
                    );
                    $ins = $pdo->prepare(
                        "INSERT IGNORE INTO system_security_detections
                         (detection_fingerprint,rule_id,rule_code,rule_name,entity_type,entity_id,score,severity,status,details_json,detected_at,created_by)
                         VALUES
                         (:fp,:rule_id,:code,:name,'user',:entity_id,:score,:severity,'open',:details,NOW(),:actor)"
                    );
                    $ins->execute([
                        'fp' => $fingerprint,
                        'rule_id' => $rule['id'],
                        'code' => $rule['code'],
                        'name' => $rule['name'],
                        'entity_id' => (string) $row['entity_id'],
                        'score' => min(100, (int) $rule['score_weight'] + (int) $row['cnt']),
                        'severity' => $rule['severity'],
                        'details' => json_encode(
                            ['event_count' => (int) $row['cnt'], 'window_minutes' => $minutes],
                            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
                        ),
                        'actor' => $actorId > 0 ? $actorId : null,
                    ]);
                    $created += $ins->rowCount();
                    $count++;
                }

                $u = $pdo->prepare(
                    'UPDATE system_detection_rules
                     SET last_run_at=NOW(),last_match_count=:matches,updated_at=NOW()
                     WHERE id=:id'
                );
                $u->execute(['matches' => $count, 'id' => $rule['id']]);
                $matched += $count;
            }

            $pdo->commit();
            return ['created' => $created, 'matched' => $matched];
        } catch (\Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            throw $e;
        }
    }

    private function allowed(string $value,array $allowed): string{return in_array($value,$allowed,true)?$value:'';}
}
