<?php

declare(strict_types=1);

namespace ABS\Modules\Tasks\Repositories;

use ABS\Core\Database;
use RuntimeException;

final class TasksRepository
{
    private bool $schemaReady=false;

    public function ensureSchema(): void
    {
        if($this->schemaReady) return;
        $statements=[
"CREATE TABLE IF NOT EXISTS abs_task_categories (
 id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(100) NOT NULL,
 slug VARCHAR(120) NOT NULL UNIQUE,
 icon VARCHAR(60) DEFAULT 'bi-tag',
 color VARCHAR(20) DEFAULT '#0d6efd',
 sort_order INT NOT NULL DEFAULT 0,
 active TINYINT(1) NOT NULL DEFAULT 1,
 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS abs_task_workflow_statuses (
 id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 name VARCHAR(100) NOT NULL,
 slug VARCHAR(120) NOT NULL UNIQUE,
 color VARCHAR(20) NOT NULL DEFAULT '#6c757d',
 icon VARCHAR(60) NOT NULL DEFAULT 'bi-circle',
 sort_order INT NOT NULL DEFAULT 0,
 active TINYINT(1) NOT NULL DEFAULT 1,
 email_template TEXT NULL,
 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS abs_tasks (
 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 title VARCHAR(255) NOT NULL,
 description TEXT NULL,
 client_source VARCHAR(50) NULL,
 client_id BIGINT NULL,
 client_label VARCHAR(255) NULL,
 client_phone VARCHAR(60) NULL,
 client_email VARCHAR(190) NULL,
 vehicle_plate VARCHAR(40) NULL,
 vehicle_vin VARCHAR(60) NULL,
 category_id INT NULL,
 assigned_user_id INT NULL,
 workflow_status_id INT NULL,
 priority ENUM('low','normal','high','urgent','critical') NOT NULL DEFAULT 'normal',
 display_order INT NOT NULL DEFAULT 0,
 status ENUM('open','completed','archived') NOT NULL DEFAULT 'open',
 due_at DATETIME NULL,
 estimated_delivery_at DATETIME NULL,
 ordered_supplier_id BIGINT NULL,
 ordered_supplier_name VARCHAR(255) NULL,
 ordered_at DATETIME NULL,
 reminder_at DATETIME NULL,
 created_by INT NULL,
 completed_by INT NULL,
 completed_at DATETIME NULL,
 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
 INDEX idx_tasks_status (status), INDEX idx_tasks_workflow (workflow_status_id), INDEX idx_tasks_assignee (assigned_user_id), INDEX idx_tasks_due (due_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS abs_task_logs (
 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 task_id BIGINT UNSIGNED NOT NULL,
 user_id INT NULL,
 action VARCHAR(60) NOT NULL,
 message TEXT NOT NULL,
 old_values LONGTEXT NULL,
 new_values LONGTEXT NULL,
 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 INDEX idx_task_logs_task (task_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
"CREATE TABLE IF NOT EXISTS abs_task_attachments (
 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
 task_id BIGINT UNSIGNED NOT NULL,
 uploaded_by INT NULL,
 original_name VARCHAR(255) NOT NULL,
 stored_name VARCHAR(255) NOT NULL,
 mime_type VARCHAR(120) NOT NULL,
 file_size BIGINT UNSIGNED NOT NULL DEFAULT 0,
 storage_path VARCHAR(500) NOT NULL,
 created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 INDEX idx_task_attachments_task (task_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
        ];
        foreach($statements as $sql) Database::prepare($sql)->execute();
        $this->addColumnIfMissing('abs_tasks','client_email','VARCHAR(190) NULL AFTER client_phone');
        $this->addColumnIfMissing('abs_tasks','workflow_status_id','INT NULL AFTER assigned_user_id');
        $this->addColumnIfMissing('abs_tasks','estimated_delivery_at','DATETIME NULL AFTER due_at');
        $this->addColumnIfMissing('abs_tasks','display_order','INT NOT NULL DEFAULT 0 AFTER priority');
        $this->addColumnIfMissing('abs_tasks','ordered_supplier_id','BIGINT NULL AFTER estimated_delivery_at');
        $this->addColumnIfMissing('abs_tasks','ordered_supplier_name','VARCHAR(255) NULL AFTER ordered_supplier_id');
        $this->addColumnIfMissing('abs_tasks','ordered_at','DATETIME NULL AFTER ordered_supplier_name');
        Database::prepare("ALTER TABLE abs_tasks MODIFY COLUMN priority ENUM('low','normal','high','urgent','critical') NOT NULL DEFAULT 'normal'")->execute();
        Database::prepare("UPDATE abs_tasks SET display_order=id WHERE display_order=0")->execute();

        $categories=[['Piese auto','piese-auto','bi-gear','#0d6efd',10],['Asigurări','asigurari','bi-shield-check','#198754',20],['Freon / Climatizare','freon-climatizare','bi-snow','#fd7e14',30],['Vopsea auto','vopsea-auto','bi-palette','#6f42c1',40],['Service auto','service-auto','bi-tools','#dc3545',50],['Revizii','revizii','bi-clipboard-check','#ffc107',60],['Diagnoză','diagnoza','bi-activity','#0dcaf0',70],['Garanții','garantii','bi-award','#6610f2',80],['Contabilitate','contabilitate','bi-calculator','#6c757d',90],['Administrativ','administrativ','bi-building','#212529',100],['Altele','altele','bi-three-dots','#adb5bd',999]];
        $q=Database::prepare('INSERT IGNORE INTO abs_task_categories(name,slug,icon,color,sort_order) VALUES(?,?,?,?,?)'); foreach($categories as $row)$q->execute($row);
        $statuses=[
            ['De ofertat','de-ofertat','#6c757d','bi-file-earmark-text',10,'Solicitarea dumneavoastră a fost înregistrată și urmează să fie ofertată.'],
            ['Ofertat','ofertat','#0d6efd','bi-send-check',20,'Oferta a fost pregătită și transmisă.'],
            ['Comandat','comandat','#fd7e14','bi-cart-check',30,'Produsele/serviciile au fost comandate.'],
            ['În lucru','in-lucru','#0dcaf0','bi-gear',40,'Solicitarea dumneavoastră este în curs de procesare.'],
            ['Pregătit / sosit','pregatit-sosit','#198754','bi-box-seam',50,'Comanda dumneavoastră a fost pregătită și poate fi ridicată.'],
            ['Ridicat / livrat','ridicat-livrat','#212529','bi-check2-circle',60,'Comanda dumneavoastră a fost ridicată/livrată.'],
            ['Anulat','anulat','#dc3545','bi-x-circle',90,'Solicitarea/comanda a fost anulată.']
        ];
        $q=Database::prepare('INSERT IGNORE INTO abs_task_workflow_statuses(name,slug,color,icon,sort_order,email_template) VALUES(?,?,?,?,?,?)'); foreach($statuses as $row)$q->execute($row);
        $dq=Database::prepare("SELECT id FROM abs_task_workflow_statuses WHERE slug='de-ofertat' LIMIT 1");$dq->execute();$defaultId=(int)($dq->fetch()['id']??0);
        if($defaultId>0){$u=Database::prepare('UPDATE abs_tasks SET workflow_status_id=? WHERE workflow_status_id IS NULL');$u->execute([$defaultId]);}
        $this->schemaReady=true;
    }

    private function addColumnIfMissing(string $table,string $column,string $definition):void
    {
        if(!$this->columnExists($table,$column)) Database::prepare("ALTER TABLE `$table` ADD COLUMN `$column` $definition")->execute();
    }

    public function users():array{$this->ensureSchema();$q=Database::prepare("SELECT id,TRIM(CONCAT(COALESCE(first_name,''),' ',COALESCE(last_name,''))) full_name,username FROM abs_users WHERE active=1 ORDER BY first_name,last_name,username");$q->execute();return $q->fetchAll();}
    public function categories(bool $activeOnly=true):array{$this->ensureSchema();$q=Database::prepare('SELECT * FROM abs_task_categories'.($activeOnly?' WHERE active=1':'').' ORDER BY sort_order,name');$q->execute();return $q->fetchAll();}
    public function workflowStatuses(bool $activeOnly=true):array{$this->ensureSchema();$q=Database::prepare('SELECT * FROM abs_task_workflow_statuses'.($activeOnly?' WHERE active=1':'').' ORDER BY sort_order,name');$q->execute();return $q->fetchAll();}

    public function suppliers():array
    {
        foreach(['md_suppliers','abs_suppliers','suppliers'] as $table){
            if(!$this->tableExists($table))continue;
            $cols=$this->columns($table);if(!in_array('id',$cols,true))continue;
            $nameCol=null;foreach(['name','company_name','supplier_name','denumire','title'] as $c)if(in_array($c,$cols,true)){$nameCol=$c;break;}
            if(!$nameCol)continue;$where=[];
            if(in_array('active',$cols,true))$where[]='active=1';
            if(in_array('is_active',$cols,true))$where[]='is_active=1';
            $q=Database::prepare("SELECT id,`$nameCol` name FROM `$table`".($where?' WHERE '.implode(' AND ',$where):'')." ORDER BY `$nameCol`");
            $q->execute();return $q->fetchAll();
        }return[];
    }

    public function summary(): array
    {
        $this->ensureSchema();
        $q = Database::prepare("SELECT
            SUM(status='open') AS open_count,
            SUM(status='open' AND due_at IS NOT NULL AND due_at < NOW()) AS overdue_count,
            SUM(status='open' AND due_at IS NOT NULL AND DATE(due_at)=CURDATE()) AS today_count,
            SUM(status='completed' AND completed_at IS NOT NULL AND DATE(completed_at)=CURDATE()) AS completed_today_count,
            SUM(status='open' AND priority='critical') AS critical_count,
            SUM(status='open' AND priority='urgent') AS urgent_count,
            SUM(status='open' AND estimated_delivery_at IS NOT NULL AND DATE(estimated_delivery_at)=CURDATE()) AS deliveries_today_count
        FROM abs_tasks");
        $q->execute();
        $row = $q->fetch() ?: [];
        return [
            'open' => (int)($row['open_count'] ?? 0),
            'overdue' => (int)($row['overdue_count'] ?? 0),
            'today' => (int)($row['today_count'] ?? 0),
            'completed_today' => (int)($row['completed_today_count'] ?? 0),
            'critical' => (int)($row['critical_count'] ?? 0),
            'urgent' => (int)($row['urgent_count'] ?? 0),
            'deliveries_today' => (int)($row['deliveries_today_count'] ?? 0),
        ];
    }

    public function tasks(array $f=[]):array
    {
        $this->ensureSchema();$where=[];$p=[];
        $status=(string)($f['status']??'open');if(!in_array($status,['open','completed','archived','all'],true))$status='open';if($status!=='all'){$where[]='t.status=:status';$p['status']=$status;}
        if(!empty($f['assigned_user_id'])){$where[]='t.assigned_user_id=:uid';$p['uid']=(int)$f['assigned_user_id'];}
        if(!empty($f['category_id'])){$where[]='t.category_id=:cat';$p['cat']=(int)$f['category_id'];}
        if(!empty($f['workflow_status_id'])){$where[]='t.workflow_status_id=:ws';$p['ws']=(int)$f['workflow_status_id'];}
        if(!empty($f['priority'])){$where[]='t.priority=:priority';$p['priority']=$f['priority'];}
        if(!empty($f['q'])){$like='%'.$f['q'].'%';$parts=[];foreach(['title','description','client_label','client_phone','client_email','vehicle_plate','vehicle_vin'] as $i=>$c){$ph='s'.$i;$parts[]="t.$c LIKE :$ph";$p[$ph]=$like;}$where[]='('.implode(' OR ',$parts).')';}
        if(($f['scope']??'')==='overdue')$where[]="t.status='open' AND t.due_at<NOW()";if(($f['scope']??'')==='today')$where[]="t.status='open' AND DATE(t.due_at)=CURDATE()";
        $orders=['manual'=>'t.display_order ASC,t.id ASC','priority_desc'=>"FIELD(t.priority,'critical','urgent','high','normal','low'),t.display_order ASC,t.id DESC",'due_asc'=>"(t.due_at IS NULL),t.due_at ASC,t.id DESC",'due_desc'=>"(t.due_at IS NULL),t.due_at DESC,t.id DESC",'created_desc'=>'t.created_at DESC,t.id DESC','created_asc'=>'t.created_at ASC,t.id ASC','completed_desc'=>"(t.completed_at IS NULL),t.completed_at DESC,t.id DESC",'completed_asc'=>"(t.completed_at IS NULL),t.completed_at ASC,t.id ASC",'delivery_asc'=>"(t.estimated_delivery_at IS NULL),t.estimated_delivery_at ASC,t.id DESC",'delivery_desc'=>"(t.estimated_delivery_at IS NULL),t.estimated_delivery_at DESC,t.id DESC"];
        $order=$orders[(string)($f['sort']??'manual')]??$orders['manual'];
        $sql="SELECT t.*,c.name category_name,c.color category_color,c.icon category_icon,ws.name workflow_status_name,ws.color workflow_status_color,ws.icon workflow_status_icon,ws.email_template,TRIM(CONCAT(COALESCE(u.first_name,''),' ',COALESCE(u.last_name,''))) assigned_name,u.username assigned_username,(SELECT COUNT(*) FROM abs_task_attachments a WHERE a.task_id=t.id) attachment_count FROM abs_tasks t LEFT JOIN abs_task_categories c ON c.id=t.category_id LEFT JOIN abs_task_workflow_statuses ws ON ws.id=t.workflow_status_id LEFT JOIN abs_users u ON u.id=t.assigned_user_id".($where?' WHERE '.implode(' AND ',$where):'')." ORDER BY $order LIMIT 500";
        $q=Database::prepare($sql);$q->execute($p);return $q->fetchAll();
    }

    public function create(array $d,int $userId):int
    {
        $this->ensureSchema();if(empty($d['workflow_status_id'])){$q=Database::prepare("SELECT id FROM abs_task_workflow_statuses WHERE slug='de-ofertat' LIMIT 1");$q->execute();$d['workflow_status_id']=(int)($q->fetch()['id']??0)?:null;}
        $max=Database::prepare("SELECT COALESCE(MAX(display_order),0)+10 next_order FROM abs_tasks WHERE status='open'");$max->execute();$d['display_order']=(int)($max->fetch()['next_order']??10);
        $sql='INSERT INTO abs_tasks(title,description,client_source,client_id,client_label,client_phone,client_email,vehicle_plate,vehicle_vin,category_id,assigned_user_id,workflow_status_id,priority,display_order,due_at,estimated_delivery_at,reminder_at,created_by) VALUES(:title,:description,:client_source,:client_id,:client_label,:client_phone,:client_email,:vehicle_plate,:vehicle_vin,:category_id,:assigned_user_id,:workflow_status_id,:priority,:display_order,:due_at,:estimated_delivery_at,:reminder_at,:created_by)';
        $q=Database::prepare($sql);$q->execute($d+['created_by'=>$userId]);$id=(int)Database::lastInsertId();$this->log($id,$userId,'created','Task creat',$d);return $id;
    }


    public function reorder(array $ids,int $userId):void
    {
        $this->ensureSchema();
        $ids=array_values(array_unique(array_filter(array_map('intval',$ids),fn($id)=>$id>0)));
        if(!$ids) throw new RuntimeException('Lista de sarcini este goală.');
        $q=Database::prepare('UPDATE abs_tasks SET display_order=? WHERE id=?');
        $position=10;
        foreach($ids as $id){$q->execute([$position,$id]);$position+=10;}
        $this->log($ids[0],$userId,'tasks_reordered','Ordinea manuală a sarcinilor a fost modificată.',['task_ids'=>$ids]);
    }

    public function toggle(int $id,int $userId):array{$t=$this->find($id);if(!$t)throw new RuntimeException('Task inexistent.');$done=$t['status']!=='completed';$q=Database::prepare('UPDATE abs_tasks SET status=?,completed_by=?,completed_at=? WHERE id=?');$q->execute([$done?'completed':'open',$done?$userId:null,$done?date('Y-m-d H:i:s'):null,$id]);$this->log($id,$userId,$done?'completed':'reopened',$done?'Task marcat ca finalizat':'Task redeschis',['status'=>$done?'completed':'open']);return['completed'=>$done];}
    public function changeWorkflowStatus(int $id,int $statusId,int $userId,array $orderData=[]):void
    {
        $old=$this->findDetailed($id);$q=Database::prepare('SELECT * FROM abs_task_workflow_statuses WHERE id=? AND active=1');$q->execute([$statusId]);$status=$q->fetch();
        if(!$old||!$status)throw new RuntimeException('Task sau status inexistent.');
        $ordered=((string)($status['slug']??'')==='comandat');
        $supplierId=(int)($orderData['ordered_supplier_id']??0)?:null;$supplierName=trim((string)($orderData['ordered_supplier_name']??''));$eta=$orderData['estimated_delivery_at']??null;
        if($ordered){
            if(!$supplierId&&$supplierName==='')throw new RuntimeException('Selectează furnizorul.');
            if(!$eta)throw new RuntimeException('Completează data și ora estimată pentru recepție.');
            Database::prepare('UPDATE abs_tasks SET workflow_status_id=?,ordered_supplier_id=?,ordered_supplier_name=?,ordered_at=NOW(),estimated_delivery_at=? WHERE id=?')->execute([$statusId,$supplierId,$supplierName?:null,$eta,$id]);
        }else Database::prepare('UPDATE abs_tasks SET workflow_status_id=? WHERE id=?')->execute([$statusId,$id]);
        $message='Status operațional schimbat în „'.$status['name'].'”';$values=['workflow_status_id'=>$statusId];
        if($ordered){$message.='; furnizor: '.($supplierName?:('#'.$supplierId)).'; recepție estimată: '.date('d.m.Y H:i',strtotime((string)$eta));$values+=['ordered_supplier_id'=>$supplierId,'ordered_supplier_name'=>$supplierName,'estimated_delivery_at'=>$eta];}
        $this->log($id,$userId,'workflow_status_changed',$message,$values,['workflow_status_id'=>$old['workflow_status_id']??null]);
    }
    public function update(int $id,array $d,int $userId):void{$old=$this->find($id);if(!$old)throw new RuntimeException('Task inexistent.');$allowed=['title','description','category_id','assigned_user_id','workflow_status_id','priority','due_at','estimated_delivery_at','reminder_at','client_label','client_phone','client_email','vehicle_plate','vehicle_vin'];$set=[];$p=['id'=>$id];foreach($allowed as $k)if(array_key_exists($k,$d)){$set[]="$k=:$k";$p[$k]=$d[$k]!==''?$d[$k]:null;}if($set){Database::prepare('UPDATE abs_tasks SET '.implode(',',$set).' WHERE id=:id')->execute($p);$this->log($id,$userId,'updated','Task actualizat',$d,$old);}}
    public function find(int $id):?array{$q=Database::prepare('SELECT * FROM abs_tasks WHERE id=?');$q->execute([$id]);$r=$q->fetch();return$r?:null;}
    public function findDetailed(int $id):?array{$q=Database::prepare('SELECT t.*,ws.name workflow_status_name,ws.email_template FROM abs_tasks t LEFT JOIN abs_task_workflow_statuses ws ON ws.id=t.workflow_status_id WHERE t.id=?');$q->execute([$id]);$r=$q->fetch();return$r?:null;}
    public function logs(int $id):array{$q=Database::prepare("SELECT l.*,TRIM(CONCAT(COALESCE(u.first_name,''),' ',COALESCE(u.last_name,''))) user_name,u.username FROM abs_task_logs l LEFT JOIN abs_users u ON u.id=l.user_id WHERE l.task_id=? ORDER BY l.id DESC");$q->execute([$id]);return$q->fetchAll();}
    public function log(int $taskId,int $userId,string $action,string $message,array $new=[],array $old=[]):void{Database::prepare('INSERT INTO abs_task_logs(task_id,user_id,action,message,old_values,new_values) VALUES(?,?,?,?,?,?)')->execute([$taskId,$userId?:null,$action,$message,$old?json_encode($old,JSON_UNESCAPED_UNICODE):null,$new?json_encode($new,JSON_UNESCAPED_UNICODE):null]);}
    public function addAttachment(int $taskId,int $userId,string $original,string $stored,string $mime,int $size,string $path):void{Database::prepare('INSERT INTO abs_task_attachments(task_id,uploaded_by,original_name,stored_name,mime_type,file_size,storage_path) VALUES(?,?,?,?,?,?,?)')->execute([$taskId,$userId?:null,$original,$stored,$mime,$size,$path]);$this->log($taskId,$userId,'attachment_added','Atașament adăugat: '.$original,['original_name'=>$original,'size'=>$size]);}
    public function attachments(int $taskId):array{$q=Database::prepare('SELECT id,task_id,original_name,mime_type,file_size,created_at FROM abs_task_attachments WHERE task_id=? ORDER BY id DESC');$q->execute([$taskId]);return$q->fetchAll();}
    public function attachment(int $id):?array{$q=Database::prepare('SELECT *,storage_path absolute_path FROM abs_task_attachments WHERE id=?');$q->execute([$id]);$r=$q->fetch();return$r?:null;}
    public function saveCategory(array $d):void{$slug=$this->slug($d['name']);if(!empty($d['id']))Database::prepare('UPDATE abs_task_categories SET name=?,slug=?,icon=?,color=?,sort_order=?,active=? WHERE id=?')->execute([$d['name'],$slug,$d['icon'],$d['color'],$d['sort_order'],$d['active'],$d['id']]);else Database::prepare('INSERT INTO abs_task_categories(name,slug,icon,color,sort_order,active) VALUES(?,?,?,?,?,?)')->execute([$d['name'],$slug,$d['icon'],$d['color'],$d['sort_order'],$d['active']]);}

    public function searchClients(string $term):array
    {
        $term=trim($term);if(mb_strlen($term)<2)return[];$like='%'.$term.'%';$out=[];
        foreach(['md_clients','abs_clients'] as $table){if(!$this->tableExists($table))continue;$cols=$this->columns($table);$search=array_values(array_intersect($cols,['name','first_name','last_name','company_name','phone','phone2','mobile','email','cnp','cui','nickname','porecla','notes','observations','additional_info','contact_name','code']));if(!$search)continue;$label=[];foreach(['name','company_name','first_name','last_name'] as $c)if(in_array($c,$cols,true))$label[]="COALESCE(`$c`,'')";if(!$label)$label=["CONCAT('Client #',`id`)"];$phone='NULL';foreach(['phone','mobile','phone2'] as $c)if(in_array($c,$cols,true)){$phone="`$c`";break;}$email=in_array('email',$cols,true)?'`email`':'NULL';$where=[];$params=[];foreach($search as $i=>$column){$ph='term'.$i;$where[]="`$column` LIKE :$ph";$params[$ph]=$like;}$q=Database::prepare("SELECT `id`,TRIM(CONCAT_WS(' ',".implode(',',$label).")) label,$phone phone,$email email FROM `$table` WHERE ".implode(' OR ',$where)." ORDER BY `id` DESC LIMIT 15");$q->execute($params);foreach($q->fetchAll() as $r)$out[]=['source'=>$table,'id'=>(int)$r['id'],'label'=>trim((string)$r['label']),'phone'=>$r['phone']??'','email'=>$r['email']??'','plate'=>'','vin'=>''];}
        if($this->tableExists('abs_vehicles')){$cols=$this->columns('abs_vehicles');$search=array_values(array_intersect($cols,['plate','vin','brand','model']));if($search){$where=[];$params=[];foreach($search as $i=>$column){$ph='vehicle_term'.$i;$where[]="`$column` LIKE :$ph";$params[$ph]=$like;}$q=Database::prepare("SELECT `id`,`plate`,`vin`,`brand`,`model` FROM `abs_vehicles` WHERE ".implode(' OR ',$where)." ORDER BY `id` DESC LIMIT 15");$q->execute($params);foreach($q->fetchAll() as $r)$out[]=['source'=>'abs_vehicles','id'=>(int)$r['id'],'label'=>trim(($r['brand']??'').' '.($r['model']??'')),'phone'=>'','email'=>'','plate'=>$r['plate']??'','vin'=>$r['vin']??''];}}
        return array_slice($out,0,30);
    }

    private function tableExists(string $t):bool{$q=Database::prepare('SELECT COUNT(*) c FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=?');$q->execute([$t]);return(int)$q->fetch()['c']>0;}
    private function columnExists(string $t,string $c):bool{$q=Database::prepare('SELECT COUNT(*) c FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name=? AND column_name=?');$q->execute([$t,$c]);return(int)$q->fetch()['c']>0;}
    private function columns(string $t):array{$q=Database::prepare("SHOW COLUMNS FROM `$t`");$q->execute();return array_column($q->fetchAll(),'Field');}
    private function slug(string $s):string{$s=iconv('UTF-8','ASCII//TRANSLIT//IGNORE',$s)?:$s;$s=strtolower((string)preg_replace('/[^a-zA-Z0-9]+/','-',$s));return trim($s,'-')?:'categorie-'.time();}
}
