<?php
declare(strict_types=1);

namespace iTz\V7;

use PDO;
use Throwable;

final class Kernel
{
    private static ?self $instance = null;
    private ?PDO $pdo;
    private array $modules = [];
    private bool $schemaReady = false;

    private function __construct(?PDO $pdo)
    {
        $this->pdo = $pdo;
        $this->loadModules();
        $this->ensureSchema();
    }

    public static function boot(?PDO $pdo = null): self
    {
        if (self::$instance === null) self::$instance = new self($pdo);
        return self::$instance;
    }

    public static function instance(): ?self { return self::$instance; }
    public function db(): ?PDO { return $this->pdo; }
    public function modules(): array { return $this->modules; }

    public function moduleEnabled(string $id, bool $default=true): bool
    {
        if (!isset($this->modules[$id])) return false;
        if (!$this->pdo instanceof PDO) return $default;
        try { $s=$this->pdo->prepare('SELECT enabled FROM itz_v7_module_state WHERE module_id=:i LIMIT 1');$s->execute([':i'=>$id]);$v=$s->fetchColumn();return $v===false?$default:(bool)$v; } catch (Throwable $e) { return $default; }
    }

    private function loadModules(): void
    {
        $root = dirname(__DIR__, 2) . '/modules';
        if (!is_dir($root)) return;
        foreach (glob($root . '/*/manifest.php') ?: [] as $file) {
            try {
                $m = require $file;
                if (!is_array($m) || empty($m['id'])) continue;
                $m += ['name'=>$m['id'],'version'=>'1.0.0','description'=>'','capabilities'=>[],'default_enabled'=>true];
                $this->modules[(string)$m['id']] = $m;
            } catch (Throwable $e) {
                error_log('[V7 module] ' . basename(dirname($file)) . ': ' . $e->getMessage());
            }
        }
    }

    private function ensureSchema(): void
    {
        if (!$this->pdo instanceof PDO || $this->schemaReady) return;
        try {
            $sql = [
                "CREATE TABLE IF NOT EXISTS itz_v7_feature_flags (flag_key VARCHAR(120) PRIMARY KEY, enabled TINYINT(1) NOT NULL DEFAULT 0, rollout_percent TINYINT UNSIGNED NOT NULL DEFAULT 100, config_json MEDIUMTEXT NULL, updated_by VARCHAR(190) NULL, updated_at INT UNSIGNED NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_events (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,event_name VARCHAR(160) NOT NULL, actor_type VARCHAR(40) NULL, actor_id VARCHAR(190) NULL, payload_json MEDIUMTEXT NULL, created_at INT UNSIGNED NOT NULL, INDEX idx_event_time(event_name,created_at), INDEX idx_actor(actor_type,actor_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_metrics (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, metric_name VARCHAR(160) NOT NULL, metric_value DOUBLE NOT NULL DEFAULT 0, tags_json TEXT NULL, created_at INT UNSIGNED NOT NULL, INDEX idx_metric_time(metric_name,created_at)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_automations (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,name VARCHAR(190) NOT NULL,event_name VARCHAR(160) NOT NULL,enabled TINYINT(1) NOT NULL DEFAULT 1,priority INT NOT NULL DEFAULT 100,conditions_json MEDIUMTEXT NULL,actions_json MEDIUMTEXT NULL,cooldown_seconds INT UNSIGNED NOT NULL DEFAULT 0,last_run_at INT UNSIGNED NOT NULL DEFAULT 0,created_at INT UNSIGNED NOT NULL DEFAULT 0,updated_at INT UNSIGNED NOT NULL DEFAULT 0,INDEX idx_event_enabled(event_name,enabled,priority)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_automation_runs (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,automation_id BIGINT UNSIGNED NOT NULL,event_id BIGINT UNSIGNED NULL,status_name VARCHAR(30) NOT NULL,message_text TEXT NULL,created_at INT UNSIGNED NOT NULL,INDEX idx_auto_time(automation_id,created_at)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_user_tags (user_id VARCHAR(64) NOT NULL,tag_key VARCHAR(100) NOT NULL,tag_value VARCHAR(255) NULL,created_at INT UNSIGNED NOT NULL DEFAULT 0,updated_at INT UNSIGNED NOT NULL DEFAULT 0,PRIMARY KEY(user_id,tag_key),INDEX idx_tag(tag_key,tag_value)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_admin_tasks (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,title VARCHAR(255) NOT NULL,description_text TEXT NULL,user_id VARCHAR(64) NULL,status_name VARCHAR(30) NOT NULL DEFAULT 'open',priority_name VARCHAR(30) NOT NULL DEFAULT 'normal',source_name VARCHAR(80) NULL,due_at INT UNSIGNED NOT NULL DEFAULT 0,created_at INT UNSIGNED NOT NULL DEFAULT 0,updated_at INT UNSIGNED NOT NULL DEFAULT 0,INDEX idx_status_due(status_name,due_at),INDEX idx_user(user_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_module_state (module_id VARCHAR(120) PRIMARY KEY,enabled TINYINT(1) NOT NULL DEFAULT 1,config_json MEDIUMTEXT NULL,updated_by VARCHAR(190) NULL,updated_at INT UNSIGNED NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
                "CREATE TABLE IF NOT EXISTS itz_v7_audit (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,category VARCHAR(80) NOT NULL,action_name VARCHAR(160) NOT NULL,actor VARCHAR(190) NULL,target VARCHAR(190) NULL,meta_json MEDIUMTEXT NULL,created_at INT UNSIGNED NOT NULL DEFAULT 0,INDEX idx_cat_time(category,created_at),INDEX idx_target(target)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
            ];
            foreach ($sql as $q) $this->pdo->exec($q);
            foreach ($this->modules as $id=>$m) {
                $s=$this->pdo->prepare("INSERT IGNORE INTO itz_v7_module_state(module_id,enabled,config_json,updated_at) VALUES(:id,:e,'{}',:t)");
                $s->execute([':id'=>$id,':e'=>!empty($m['default_enabled'])?1:0,':t'=>time()]);
            }
            $defaults = [
                'automation_engine'=>1,'customer_360'=>1,'observability'=>1,'smart_notifications'=>1,
                'backend_failover'=>0,'experimental_ui'=>0,'maintenance_guard'=>1,'commerce_kill_switch'=>0,'payments_kill_switch'=>0,'audit_everything'=>1
            ];
            foreach($defaults as $k=>$v){$s=$this->pdo->prepare("INSERT IGNORE INTO itz_v7_feature_flags(flag_key,enabled,rollout_percent,config_json,updated_at) VALUES(:k,:e,100,'{}',:t)");$s->execute([':k'=>$k,':e'=>$v,':t'=>time()]);}
            $this->schemaReady = true;
        } catch (Throwable $e) {
            error_log('[V7 schema] ' . $e->getMessage());
        }
    }

    public function feature(string $key, bool $default=false, ?string $subjectId=null): bool
    {
        if (!$this->pdo instanceof PDO) return $default;
        try {
            $s=$this->pdo->prepare('SELECT enabled,rollout_percent FROM itz_v7_feature_flags WHERE flag_key=:k LIMIT 1');$s->execute([':k'=>$key]);$r=$s->fetch(PDO::FETCH_ASSOC);
            if(!$r) return $default; if(!(bool)$r['enabled']) return false;
            $pct=max(0,min(100,(int)$r['rollout_percent'])); if($pct>=100||$subjectId===null||$subjectId==='') return true;
            $bucket=(int)(hexdec(substr(hash('sha256',$key.'|'.$subjectId),0,8))%100); return $bucket<$pct;
        } catch(Throwable $e){return $default;}
    }

    public function emit(string $event, array $payload=[], string $actorType='system', ?string $actorId=null): ?int
    {
        if (!$this->pdo instanceof PDO) return null;
        try {
            $s=$this->pdo->prepare('INSERT INTO itz_v7_events(event_name,actor_type,actor_id,payload_json,created_at) VALUES(:e,:at,:ai,:p,:t)');
            $s->execute([':e'=>$event,':at'=>$actorType,':ai'=>$actorId,':p'=>json_encode($payload,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':t'=>time()]);
            $id=(int)$this->pdo->lastInsertId();
            if($this->moduleEnabled('automation',true) && $this->feature('automation_engine',true,$actorId)) $this->runAutomations($event,$payload,$id,$actorId);
            return $id;
        } catch(Throwable $e){error_log('[V7 event] '.$e->getMessage());return null;}
    }

    public function metric(string $name, float $value=1.0, array $tags=[]): void
    {
        if (!$this->pdo instanceof PDO || !$this->moduleEnabled('observability',true) || !$this->feature('observability',true)) return;
        try{$s=$this->pdo->prepare('INSERT INTO itz_v7_metrics(metric_name,metric_value,tags_json,created_at) VALUES(:n,:v,:j,:t)');$s->execute([':n'=>$name,':v'=>$value,':j'=>json_encode($tags,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':t'=>time()]);}catch(Throwable $e){}
    }

    public function audit(string $category,string $action,?string $target=null,array $meta=[]): void
    {
        if (!$this->pdo instanceof PDO || !$this->feature('audit_everything',true)) return;
        foreach($meta as $k=>$v) if(preg_match('/token|secret|password|api.?key|private/i',(string)$k)) $meta[$k]='***';
        try{$s=$this->pdo->prepare('INSERT INTO itz_v7_audit(category,action_name,actor,target,meta_json,created_at) VALUES(:c,:a,:u,:tg,:m,:t)');$s->execute([':c'=>$category,':a'=>$action,':u'=>(string)($_SESSION['user']??'system'),':tg'=>$target,':m'=>json_encode($meta,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),':t'=>time()]);}catch(Throwable $e){}
    }

    private function runAutomations(string $event,array $payload,int $eventId,?string $actorId): void
    {
        try{
            $s=$this->pdo->prepare('SELECT * FROM itz_v7_automations WHERE event_name=:e AND enabled=1 ORDER BY priority ASC,id ASC');$s->execute([':e'=>$event]);
            foreach($s->fetchAll(PDO::FETCH_ASSOC)?:[] as $a){
                if((int)$a['cooldown_seconds']>0 && (time()-(int)$a['last_run_at'])<(int)$a['cooldown_seconds']) continue;
                $conditions=json_decode((string)($a['conditions_json']??'[]'),true); if(!is_array($conditions))$conditions=[];
                if(!$this->conditionsPass($conditions,$payload,$actorId)) continue;
                $actions=json_decode((string)($a['actions_json']??'[]'),true); if(!is_array($actions))$actions=[];
                $ok=true;$messages=[];
                foreach($actions as $action){$r=$this->executeAction(is_array($action)?$action:[],$payload,$actorId);$ok=$ok&&$r['ok'];$messages[]=$r['message'];}
                $this->pdo->prepare('INSERT INTO itz_v7_automation_runs(automation_id,event_id,status_name,message_text,created_at) VALUES(:a,:e,:s,:m,:t)')->execute([':a'=>$a['id'],':e'=>$eventId,':s'=>$ok?'ok':'partial',':m'=>implode(' | ',$messages),':t'=>time()]);
                $this->pdo->prepare('UPDATE itz_v7_automations SET last_run_at=:t WHERE id=:id')->execute([':t'=>time(),':id'=>$a['id']]);
            }
        }catch(Throwable $e){error_log('[V7 automation] '.$e->getMessage());}
    }

    private function conditionsPass(array $conditions,array $payload,?string $actorId): bool
    {
        foreach($conditions as $c){if(!is_array($c))continue;$field=(string)($c['field']??'');$op=(string)($c['op']??'eq');$want=$c['value']??null;$got=$field==='actor_id'?$actorId:($payload[$field]??null);
            $pass=match($op){'eq'=>(string)$got===(string)$want,'neq'=>(string)$got!==(string)$want,'gt'=>(float)$got>(float)$want,'gte'=>(float)$got>=(float)$want,'lt'=>(float)$got<(float)$want,'lte'=>(float)$got<=(float)$want,'contains'=>str_contains(function_exists('mb_strtolower')?mb_strtolower((string)$got,'UTF-8'):strtolower((string)$got),function_exists('mb_strtolower')?mb_strtolower((string)$want,'UTF-8'):strtolower((string)$want)),'in'=>is_array($want)&&in_array((string)$got,array_map('strval',$want),true),default=>false}; if(!$pass)return false;}
        return true;
    }

    private function executeAction(array $action,array $payload,?string $actorId): array
    {
        $type=(string)($action['type']??'log');
        try{
            if($type==='add_tag'){$uid=(string)($action['user_id']??$actorId??$payload['user_id']??'');$key=(string)($action['key']??'');if($uid===''||$key==='')throw new \RuntimeException('tag parameters missing');$value=(string)($action['value']??'1');$s=$this->pdo->prepare('INSERT INTO itz_v7_user_tags(user_id,tag_key,tag_value,created_at,updated_at) VALUES(:u,:k,:v,:created_at,:updated_at) ON DUPLICATE KEY UPDATE tag_value=VALUES(tag_value),updated_at=VALUES(updated_at)');$s->execute([':u'=>$uid,':k'=>$key,':v'=>$value,':created_at'=>time(),':updated_at'=>time()]);return ['ok'=>true,'message'=>'tag'];}
            if($type==='create_task'){$title=(string)($action['title']??'Automation task');$uid=(string)($action['user_id']??$actorId??'');$s=$this->pdo->prepare('INSERT INTO itz_v7_admin_tasks(title,description_text,user_id,priority_name,source_name,created_at,updated_at) VALUES(:t,:d,:u,:p,:s,:created_at,:updated_at)');$s->execute([':t'=>$title,':d'=>(string)($action['description']??''),':u'=>$uid!==''?$uid:null,':p'=>(string)($action['priority']??'normal'),':s'=>'automation',':created_at'=>time(),':updated_at'=>time()]);return ['ok'=>true,'message'=>'task'];}
            if($type==='telegram_admin' && function_exists('telegram')){$admin=(string)($GLOBALS['adminnumber']??'');if($admin==='')throw new \RuntimeException('admin id missing');$text=(string)($action['text']??('Event: '.json_encode($payload,JSON_UNESCAPED_UNICODE)));$r=\telegram('sendMessage',['chat_id'=>$admin,'text'=>$text,'parse_mode'=>'HTML']);return ['ok'=>!empty($r['ok']),'message'=>'telegram_admin'];}
            $this->audit('automation','action.'.$type,$actorId,['payload'=>$payload]);return ['ok'=>true,'message'=>'log'];
        }catch(Throwable $e){return ['ok'=>false,'message'=>$e->getMessage()];}
    }

    public function customer360(string $userId): array
    {
        if (!$this->pdo instanceof PDO) return [];
        $out=['user_id'=>$userId,'user'=>null,'invoices'=>[],'services'=>[],'tags'=>[],'score'=>50,'signals'=>[]];
        try{$s=$this->pdo->prepare('SELECT * FROM user WHERE id=:u LIMIT 1');$s->execute([':u'=>$userId]);$out['user']=$s->fetch(PDO::FETCH_ASSOC)?:null;}catch(Throwable $e){}
        try{$s=$this->pdo->prepare('SELECT id_invoice,Status,price,Service_location,time FROM invoice WHERE id_user=:u ORDER BY id DESC LIMIT 20');$s->execute([':u'=>$userId]);$out['invoices']=$s->fetchAll(PDO::FETCH_ASSOC)?:[];}catch(Throwable $e){}
        try{$s=$this->pdo->prepare('SELECT * FROM service_other WHERE id_user=:u ORDER BY id DESC LIMIT 20');$s->execute([':u'=>$userId]);$out['services']=$s->fetchAll(PDO::FETCH_ASSOC)?:[];}catch(Throwable $e){}
        try{$s=$this->pdo->prepare('SELECT tag_key,tag_value FROM itz_v7_user_tags WHERE user_id=:u');$s->execute([':u'=>$userId]);foreach($s->fetchAll(PDO::FETCH_ASSOC)?:[] as $r)$out['tags'][$r['tag_key']]=$r['tag_value'];}catch(Throwable $e){}
        $active=0;$failed=0;foreach($out['invoices'] as $i){$st=strtolower((string)($i['Status']??''));if(in_array($st,['active','paid'],true))$active++;if(in_array($st,['unsuccessful','failed','cancelled'],true))$failed++;}
        $score=50+min(35,$active*7)-min(35,$failed*8);if($active>=3)$out['signals'][]='وفادار';if($failed>=2)$out['signals'][]='ریسک پرداخت';if(count($out['services'])>=5)$out['signals'][]='کاربر پرمصرف';$out['score']=max(0,min(100,$score));
        return $out;
    }
}
