<?php

declare(strict_types=1);

namespace ABS\Modules\Tasks\Controllers;

use ABS\Core\Controller;
use ABS\Modules\Tasks\Repositories\TasksRepository;
use RuntimeException;
use Throwable;

final class TasksController extends Controller
{
    private TasksRepository $repo;

    public function __construct()
    {
        $this->repo = new TasksRepository();
        $this->repo->ensureSchema();
    }

    private function uid(): int
    {
        return (int) ($_SESSION['user']['id'] ?? $_SESSION['user_id'] ?? 0);
    }

    public function index(): never
    {
        $this->view('tasks/index', [
            'title' => 'Sarcini',
            'tasks' => $this->repo->tasks($_GET + ['status' => 'open']),
            'users' => $this->repo->users(),
            'categories' => $this->repo->categories(false),
            'workflowStatuses' => $this->repo->workflowStatuses(false),
            'suppliers' => $this->repo->suppliers(),
        ]);
    }

    public function dashboardWidget(): never
    {
        header('Content-Type: text/html; charset=utf-8');
        $tasks = $this->repo->tasks($_GET + ['status' => 'open']);
        $users = $this->repo->users();
        $categories = $this->repo->categories();
        $workflowStatuses = $this->repo->workflowStatuses();
        $suppliers = $this->repo->suppliers();
        require dirname(__DIR__) . '/Views/tasks/widget.php';
        exit;
    }

    public function summaryJson(): never
    {
        $this->json(['ok' => true, 'summary' => $this->repo->summary()]);
    }

    public function listJson(): never { $this->json(['ok' => true, 'tasks' => $this->repo->tasks($_GET)]); }

    public function lookups(): never
    {
        $this->json(['ok' => true, 'users' => $this->repo->users(), 'categories' => $this->repo->categories(false), 'workflow_statuses' => $this->repo->workflowStatuses(false), 'suppliers' => $this->repo->suppliers()]);
    }

    public function clientSearch(): never
    {
        $this->json(['ok' => true, 'results' => $this->repo->searchClients((string) ($_GET['q'] ?? ''))]);
    }

    public function store(): never
    {
        try {
            $title = trim((string) ($_POST['title'] ?? ''));
            if ($title === '') throw new RuntimeException('Titlul este obligatoriu.');
            $priority = (string) ($_POST['priority'] ?? 'normal');
            if (!in_array($priority, ['low','normal','high','urgent','critical'], true)) $priority = 'normal';

            $id = $this->repo->create([
                'title' => $title,
                'description' => trim((string) ($_POST['description'] ?? '')),
                'client_source' => trim((string) ($_POST['client_source'] ?? '')),
                'client_id' => (int) ($_POST['client_id'] ?? 0) ?: null,
                'client_label' => trim((string) ($_POST['client_label'] ?? '')),
                'client_phone' => trim((string) ($_POST['client_phone'] ?? '')),
                'client_email' => trim((string) ($_POST['client_email'] ?? '')),
                'vehicle_plate' => trim((string) ($_POST['vehicle_plate'] ?? '')),
                'vehicle_vin' => trim((string) ($_POST['vehicle_vin'] ?? '')),
                'category_id' => (int) ($_POST['category_id'] ?? 0) ?: null,
                'assigned_user_id' => (int) ($_POST['assigned_user_id'] ?? 0) ?: null,
                'workflow_status_id' => (int) ($_POST['workflow_status_id'] ?? 0) ?: null,
                'priority' => $priority,
                'due_at' => $this->dateTime($_POST['due_at'] ?? null),
                'estimated_delivery_at' => $this->dateTime($_POST['estimated_delivery_at'] ?? null),
                'reminder_at' => $this->dateTime($_POST['reminder_at'] ?? null),
            ], $this->uid());

            $uploaded = $this->saveUploadedFiles($id);
            $this->json(['ok' => true, 'id' => $id, 'uploaded' => $uploaded, 'message' => 'Task creat.']);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function uploadAttachments(string $id): never
    {
        try { $this->json(['ok' => true, 'uploaded' => $this->saveUploadedFiles((int)$id)]); }
        catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function downloadAttachment(string $id): never
    {
        $a = $this->repo->attachment((int)$id);
        if (!$a || !is_file($a['absolute_path'])) { http_response_code(404); exit('Fișier inexistent.'); }
        header('Content-Type: '.$a['mime_type']);
        header('Content-Length: '.filesize($a['absolute_path']));
        header('Content-Disposition: attachment; filename="'.rawurlencode($a['original_name']).'"');
        readfile($a['absolute_path']); exit;
    }

    public function previewAttachment(string $id): never
    {
        $a = $this->repo->attachment((int)$id);
        if (!$a || !is_file($a['absolute_path'])) { http_response_code(404); exit('Fișier inexistent.'); }
        $previewable = str_starts_with((string)$a['mime_type'], 'image/') || $a['mime_type'] === 'application/pdf' || $a['mime_type'] === 'text/plain';
        if (!$previewable) { http_response_code(415); exit('Acest tip de document nu poate fi previzualizat direct. Folosește descărcarea.'); }
        header('Content-Type: '.$a['mime_type']);
        header('Content-Length: '.filesize($a['absolute_path']));
        header('Content-Disposition: inline; filename="'.rawurlencode($a['original_name']).'"');
        header('X-Content-Type-Options: nosniff');
        readfile($a['absolute_path']); exit;
    }

    public function ocrAttachment(string $id): never
    {
        try {
            $a = $this->repo->attachment((int)$id);
            if (!$a || !is_file($a['absolute_path'])) throw new RuntimeException('Fișier inexistent.');
            $mime = (string)$a['mime_type'];
            if (!str_starts_with($mime, 'image/') && $mime !== 'application/pdf') {
                throw new RuntimeException('OCR este disponibil pentru imagini și PDF-uri scanate.');
            }
            if (!function_exists('shell_exec')) throw new RuntimeException('Funcția shell_exec este dezactivată în PHP.');
            $tesseract = $this->findExecutable('tesseract', [
                getenv('TESSERACT_PATH') ?: '',
                'C:\Program Files\Tesseract-OCR\tesseract.exe',
                'C:\Program Files (x86)\Tesseract-OCR\tesseract.exe'
            ]);
            if (!$tesseract) throw new RuntimeException('Tesseract OCR nu este instalat. Instalează Tesseract și setează variabila TESSERACT_PATH.');

            $source = $a['absolute_path'];
            $tempImage = null;
            if ($mime === 'application/pdf') {
                $pdftoppm = $this->findExecutable('pdftoppm', [getenv('PDFTOPPM_PATH') ?: '']);
                if (!$pdftoppm) throw new RuntimeException('Pentru OCR pe PDF este necesar și pdftoppm (Poppler). OCR pe imagini funcționează fără Poppler.');
                $prefix = sys_get_temp_dir().DIRECTORY_SEPARATOR.'abs_ocr_'.bin2hex(random_bytes(6));
                $cmd = escapeshellarg($pdftoppm).' -f 1 -singlefile -png -r 220 '.escapeshellarg($source).' '.escapeshellarg($prefix).' 2>&1';
                shell_exec($cmd);
                $tempImage = $prefix.'.png';
                if (!is_file($tempImage)) throw new RuntimeException('PDF-ul nu a putut fi convertit pentru OCR.');
                $source = $tempImage;
            }

            $lang = preg_replace('/[^a-zA-Z+_-]/', '', (string)($_POST['lang'] ?? 'ron+eng')) ?: 'ron+eng';
            $cmd = escapeshellarg($tesseract).' '.escapeshellarg($source).' stdout -l '.escapeshellarg($lang).' --psm 6 2>&1';
            $text = trim((string)shell_exec($cmd));
            if ($tempImage && is_file($tempImage)) @unlink($tempImage);
            if ($text === '') throw new RuntimeException('OCR nu a identificat text în document.');
            $this->repo->log((int)$a['task_id'], $this->uid(), 'ocr_processed', 'OCR executat pentru: '.$a['original_name'], ['attachment_id'=>(int)$a['id']]);
            $this->json(['ok'=>true,'text'=>$text,'file'=>$a['original_name']]);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }


    public function reorder(): never
    {
        try {
            $raw=(string)($_POST['ids']??'');
            $ids=$raw!==''?explode(',',$raw):[];
            $this->repo->reorder($ids,$this->uid());
            $this->json(['ok'=>true,'message'=>'Ordinea a fost salvată.']);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(),422); }
    }

    public function toggle(string $id): never
    {
        try { $this->json(['ok' => true] + $this->repo->toggle((int)$id, $this->uid())); }
        catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function update(string $id): never
    {
        try {
            $data = $_POST;
            foreach (['due_at', 'estimated_delivery_at', 'reminder_at'] as $field) {
                if (array_key_exists($field, $data)) $data[$field] = $this->dateTime($data[$field]);
            }
            if (isset($data['priority']) && !in_array((string)$data['priority'], ['low','normal','high','urgent','critical'], true)) {
                $data['priority'] = 'normal';
            }
            $this->repo->update((int)$id, $data, $this->uid());
            $uploaded = $this->saveUploadedFiles((int)$id);
            $this->json(['ok' => true, 'uploaded' => $uploaded, 'message' => 'Task actualizat.']);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function changeWorkflowStatus(string $id): never
    {
        try {
            $statusId = (int)($_POST['workflow_status_id'] ?? 0);
            if ($statusId < 1) throw new RuntimeException('Status invalid.');
            $orderData=['ordered_supplier_id'=>(int)($_POST['ordered_supplier_id']??0)?:null,'ordered_supplier_name'=>trim((string)($_POST['ordered_supplier_name']??'')),'estimated_delivery_at'=>$this->dateTime($_POST['estimated_delivery_at']??null)];
            $this->repo->changeWorkflowStatus((int)$id,$statusId,$this->uid(),$orderData);
            $this->json(['ok'=>true]);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function sendStatusEmail(string $id): never
    {
        try {
            $task = $this->repo->findDetailed((int)$id);
            if (!$task) throw new RuntimeException('Task inexistent.');
            $email = trim((string)($task['client_email'] ?? ''));
            if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new RuntimeException('Clientul nu are o adresă de email validă.');

            $subject = trim((string)($_POST['subject'] ?? 'Actualizare comandă - ADANY IMPEX SRL'));
            $message = trim((string)($_POST['message'] ?? ''));
            if ($message === '') $message = $this->defaultEmailMessage($task);
            $from = trim((string)($_POST['from'] ?? '')) ?: 'noreply@adanyimpex.ro';
            $headers = "MIME-Version: 1.0\r\nContent-type: text/html; charset=UTF-8\r\nFrom: ADANY IMPEX SRL <{$from}>\r\n";
            $html = '<div style="font-family:Arial,sans-serif;line-height:1.55">'.nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8')).'</div>';
            $sent = @mail($email, '=?UTF-8?B?'.base64_encode($subject).'?=', $html, $headers);
            if (!$sent) throw new RuntimeException('Emailul nu a putut fi trimis prin configurația PHP mail(). Verifică setările SMTP/sendmail din XAMPP.');
            $this->repo->log((int)$id, $this->uid(), 'email_sent', 'Email trimis către '.$email, ['subject'=>$subject,'email'=>$email]);
            $this->json(['ok'=>true,'message'=>'Email trimis către '.$email]);
        } catch (Throwable $e) { $this->errorJson($e->getMessage(), 422); }
    }

    public function logJson(string $id): never
    {
        $this->json(['ok'=>true,'logs'=>$this->repo->logs((int)$id),'attachments'=>$this->repo->attachments((int)$id)]);
    }

    public function saveCategory(): never
    {
        try {
            $name=trim((string)($_POST['name']??'')); if($name==='') throw new RuntimeException('Numele categoriei este obligatoriu.');
            $this->repo->saveCategory(['id'=>(int)($_POST['id']??0),'name'=>$name,'icon'=>trim((string)($_POST['icon']??'bi-tag')),'color'=>trim((string)($_POST['color']??'#0d6efd')),'sort_order'=>(int)($_POST['sort_order']??0),'active'=>isset($_POST['active'])?1:0]);
            $this->json(['ok'=>true]);
        } catch(Throwable $e){$this->errorJson($e->getMessage(),422);}
    }

    private function saveUploadedFiles(int $taskId): int
    {
        if (empty($_FILES['attachments']) || !is_array($_FILES['attachments']['name'])) return 0;
        $allowed = ['image/jpeg','image/png','image/webp','application/pdf','application/msword','application/vnd.openxmlformats-officedocument.wordprocessingml.document','application/vnd.ms-excel','application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','text/plain'];
        $count=0; $base=dirname(__DIR__,3).'/storage/task-attachments/'.$taskId;
        if(!is_dir($base) && !mkdir($base,0775,true) && !is_dir($base)) throw new RuntimeException('Nu pot crea directorul pentru atașamente.');
        $names=$_FILES['attachments']['name'];
        foreach($names as $i=>$name){
            $error=(int)($_FILES['attachments']['error'][$i]??UPLOAD_ERR_NO_FILE); if($error===UPLOAD_ERR_NO_FILE) continue;
            if($error!==UPLOAD_ERR_OK) throw new RuntimeException('Eroare la încărcarea fișierului '.$name.'.');
            $size=(int)$_FILES['attachments']['size'][$i]; if($size>15*1024*1024) throw new RuntimeException('Fișierul '.$name.' depășește 15 MB.');
            $tmp=$_FILES['attachments']['tmp_name'][$i]; $mime=(new \finfo(FILEINFO_MIME_TYPE))->file($tmp) ?: 'application/octet-stream';
            if(!in_array($mime,$allowed,true)) throw new RuntimeException('Tip de fișier neacceptat: '.$name);
            $safe=preg_replace('/[^a-zA-Z0-9._-]+/','_',basename((string)$name));
            $stored=bin2hex(random_bytes(8)).'_'.$safe; $path=$base.'/'.$stored;
            if(!move_uploaded_file($tmp,$path)) throw new RuntimeException('Nu am putut salva '.$name.'.');
            $this->repo->addAttachment($taskId,$this->uid(),(string)$name,$stored,$mime,$size,$path); $count++;
        }
        return $count;
    }

    private function defaultEmailMessage(array $task): string
    {
        $name = trim((string)($task['client_label'] ?? '')) ?: 'Stimate client';
        $status = mb_strtolower((string)($task['workflow_status_name'] ?? 'actualizată'));
        $eta = !empty($task['estimated_delivery_at']) ? "\nOra estimată: ".date('d.m.Y H:i', strtotime($task['estimated_delivery_at'])).'.' : '';
        return "{$name},\n\nVă informăm că solicitarea/comanda dumneavoastră are statusul: {$status}.{$eta}\n\nVă mulțumim,\nADANY IMPEX SRL";
    }

    private function dateTime(mixed $value): ?string
    {
        $value=trim((string)$value); if($value==='') return null; $value=str_replace('T',' ',$value); return strlen($value)===16?$value.':00':$value;
    }

    private function findExecutable(string $name, array $candidates=[]): ?string
    {
        foreach ($candidates as $candidate) if ($candidate && is_file($candidate)) return $candidate;
        $probe = PHP_OS_FAMILY === 'Windows' ? 'where '.escapeshellarg($name).' 2>NUL' : 'command -v '.escapeshellarg($name).' 2>/dev/null';
        $out = trim((string)shell_exec($probe));
        if ($out !== '') {
            $first = preg_split('/\r?\n/', $out)[0] ?? '';
            if ($first !== '' && is_file($first)) return $first;
        }
        return null;
    }

    private function errorJson(string $message,int $status): never
    {
        http_response_code($status); $this->json(['ok'=>false,'message'=>$message]);
    }
}
