<?php
declare(strict_types=1);

namespace App\Controllers\Web;

use App\Middleware\AuthSession;
use App\Middleware\TenantScope;
use App\Lib\Pdf;
use App\Models\Asset;
use App\Models\Attachment;
use App\Models\Incident;
use App\Models\Part;
use App\Models\Permit;
use App\Models\Site;
use App\Models\Tenant;
use App\Models\User;
use App\Lib\XLSXWriter;
use App\Models\WorkOrder;
use Core\Controller;
use Core\Session;
use Core\Tool;

final class WorkOrdersController extends Controller
{
    public function middleware(): array
    {
        return [AuthSession::class, TenantScope::class];
    }

    private function tenantId(): int
    {
        return (int) ($_SESSION['tenant_id'] ?? 0);
    }

    public function index(): void
    {
        $filters = [
            'wo_type'     => $_GET['wo_type']     ?? null,
            'priority'    => $_GET['priority']    ?? null,
            'status'      => $_GET['status']      ?? null,
            'assigned_to' => $_GET['assigned_to'] ?? null,
            'q'           => $_GET['q']           ?? null,
        ];
        $sort      = $_GET['sort'] ?? 'newest';
        $tenantId  = $this->tenantId();
        $active    = array_filter($filters);
        $perPage   = (int) ($_GET['per_page'] ?? 25);
        $perPage   = max(10, min($perPage, 100));         // clamp — no ?per_page=99999
        $page      = max(1, (int) ($_GET['page'] ?? 1));
        $total     = WorkOrder::countForTenant($tenantId, $active);
        $lastPage  = max(1, (int) ceil($total / $perPage));
        if ($page > $lastPage) $page = $lastPage;         // caller asked for a page past the end

        $this->render('work-orders/index', [
            'orders'  => WorkOrder::listForTenant($tenantId, $active, $perPage, ($page - 1) * $perPage, $sort),
            'sort'    => $sort,
            'users'   => User::listForTenant($tenantId),
            'filters' => $filters,
            'flash'   => Session::flash('wo_msg'),
            'pager'   => [
                'total'    => $total,
                'per_page' => $perPage,
                'page'     => $page,
                'last'     => $lastPage,
                'from'     => $total > 0 ? ($page - 1) * $perPage + 1 : 0,
                'to'       => min($page * $perPage, $total),
            ],
        ]);
    }

    public function create(): void
    {
        $tenantId = $this->tenantId();
        $this->render('work-orders/form', [
            'wo'        => null,
            'sites'     => Site::optionsForTenant($tenantId),
            'assets'    => Asset::optionsForTenant($tenantId),
            'users'     => User::listForTenant($tenantId),
            'incidents' => Incident::listForTenant($tenantId, ['status' => 'open']),
            'csrf'      => Tool::generateCsrf(),
        ]);
    }

    public function edit(int $id): void
    {
        $tenantId = $this->tenantId();
        $wo = WorkOrder::findInTenant($tenantId, $id);
        if (!$wo) { Session::flash('wo_msg', 'Work order not found.'); redirect('/WorkOrders'); }
        $this->render('work-orders/form', [
            'wo'        => $wo,
            'sites'     => Site::optionsForTenant($tenantId),
            'assets'    => Asset::optionsForTenant($tenantId),
            'users'     => User::listForTenant($tenantId),
            'incidents' => Incident::listForTenant($tenantId),
            'csrf'      => Tool::generateCsrf(),
        ]);
    }

    public function save(): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/WorkOrders');
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) {
            Session::flash('wo_msg', 'Session expired.'); redirect('/WorkOrders');
        }
        $tenantId = $this->tenantId();
        $userId   = (int) (Session::user()['user_id'] ?? 0);
        $id       = (int) ($_POST['work_order_id'] ?? 0);

        if (trim((string) ($_POST['title'] ?? '')) === '') {
            Session::flash('wo_msg', 'Title is required.');
            redirect($id > 0 ? "/WorkOrders/edit/{$id}" : '/WorkOrders/create');
        }

        if ($id > 0) {
            WorkOrder::update($tenantId, $id, $_POST);
            Session::flash('wo_msg', 'Work order updated.');
        } else {
            $id = WorkOrder::create($tenantId, $userId, $_POST);
            Session::flash('wo_msg', 'Work order created.');
        }
        redirect("/WorkOrders/show/{$id}");
    }

    public function show(int $id): void
    {
        $tenantId = $this->tenantId();
        $wo = WorkOrder::findInTenant($tenantId, $id);
        if (!$wo) { Session::flash('wo_msg', 'Not found.'); redirect('/WorkOrders'); }
        $this->render('work-orders/show', [
            'wo'                => $wo,
            'tasks'             => WorkOrder::tasksFor($id),
            'attachments'       => Attachment::forOwner($tenantId, 'work_order', $id),
            'partsUsed'         => Part::partsUsedOnWo($id),
            'partOptions'       => Part::optionsForTenant($tenantId),
            'materialCost'      => Part::totalMaterialCostForWo($id),
            'permits'           => Permit::forWorkOrder($tenantId, $id),
            'hasApprovedPermit' => Permit::hasApprovedForWo($tenantId, $id),
            'ownerType'         => 'work_order',
            'ownerId'           => $id,
            'flash'             => Session::flash('wo_msg') ?: Session::flash('attach_msg'),
            'csrf'              => Tool::generateCsrf(),
        ]);
    }

    /**
     * Consume a part on this WO — updates stock ledger and wo_parts_used denorm.
     */
    public function consumePart(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect("/WorkOrders/show/{$id}");
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect("/WorkOrders/show/{$id}");
        $tenantId = $this->tenantId();
        $userId   = (int) (Session::user()['user_id'] ?? 0) ?: null;
        $partId   = (int) ($_POST['part_id'] ?? 0);
        $qty      = (float) ($_POST['quantity'] ?? 0);
        if ($partId > 0 && $qty > 0) {
            Part::consumeToWo($tenantId, $partId, $id, $qty, $userId);
            Session::flash('wo_msg', 'Part consumption logged.');
        }
        redirect("/WorkOrders/show/{$id}");
    }

    public function addTask(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect("/WorkOrders/show/{$id}");
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect("/WorkOrders/show/{$id}");
        $tenantId = $this->tenantId();
        $wo = WorkOrder::findInTenant($tenantId, $id);
        if (!$wo) redirect('/WorkOrders');
        if (trim((string) ($_POST['title'] ?? '')) !== '') {
            WorkOrder::addTask($id, $_POST);
            Session::flash('wo_msg', 'Task added.');
        }
        redirect("/WorkOrders/show/{$id}");
    }

    public function toggleTask(int $taskId): void
    {
        $tenantId = $this->tenantId();
        $userId   = (int) (Session::user()['user_id'] ?? 0);
        $task = WorkOrder::taskInTenant($tenantId, $taskId);
        if (!$task) redirect('/WorkOrders');
        if ($task['is_done']) {
            WorkOrder::reopenTask($taskId);
        } else {
            WorkOrder::completeTask($taskId, $userId, $_GET['note'] ?? null);
        }
        redirect('/WorkOrders/show/' . (int) $task['work_order_id']);
    }

    /**
     * Structured task submission — carries response_value + remedy_note.
     * POST /WorkOrders/submitTask/:taskId
     */
    public function submitTask(int $taskId): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect('/WorkOrders');
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect('/WorkOrders');
        $tenantId = $this->tenantId();
        $userId   = (int) (Session::user()['user_id'] ?? 0);
        $task = WorkOrder::taskInTenant($tenantId, $taskId);
        if (!$task) redirect('/WorkOrders');

        $response = trim((string) ($_POST['response_value'] ?? ''));
        $note     = trim((string) ($_POST['result_note']    ?? '')) ?: null;
        $remedy   = trim((string) ($_POST['remedy_note']    ?? '')) ?: null;

        // Required tasks must have a response value.
        if (!empty($task['is_required']) && $response === '' && $task['input_type'] !== 'checkbox') {
            Session::flash('wo_msg', 'A response is required for: ' . $task['title']);
            redirect('/WorkOrders/show/' . (int) $task['work_order_id']);
        }

        WorkOrder::completeTask($taskId, $userId, $note, $response !== '' ? $response : null, $remedy);
        redirect('/WorkOrders/show/' . (int) $task['work_order_id']);
    }

    public function deleteTask(int $taskId): void
    {
        $tenantId = $this->tenantId();
        $task = WorkOrder::taskInTenant($tenantId, $taskId);
        if (!$task) redirect('/WorkOrders');
        WorkOrder::deleteTask($taskId);
        Session::flash('wo_msg', 'Task removed.');
        redirect('/WorkOrders/show/' . (int) $task['work_order_id']);
    }

    /**
     * Close (complete) a checklist / work order.
     * POST /WorkOrders/close/{id}
     */
    public function close(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') redirect("/WorkOrders/show/{$id}");
        if (!Tool::checkCsrf($_POST['_csrf'] ?? null)) redirect("/WorkOrders/show/{$id}");
        $tenantId = $this->tenantId();
        $wo = WorkOrder::findInTenant($tenantId, $id);
        if (!$wo) { Session::flash('wo_msg', 'Not found.'); redirect('/WorkOrders'); }
        WorkOrder::update($tenantId, $id, ['status' => 'completed']);
        Session::flash('wo_msg', 'Checklist closed.');
        redirect("/WorkOrders/show/{$id}");
    }

    public function delete(int $id): void
    {
        WorkOrder::softDelete($this->tenantId(), $id);
        Session::flash('wo_msg', 'Work order deleted.');
        redirect('/WorkOrders');
    }

    public function export(): void
    {
        $tenantId = $this->tenantId();
        $filters = [
            'wo_type'     => $_GET['wo_type']     ?? null,
            'priority'    => $_GET['priority']    ?? null,
            'status'      => $_GET['status']      ?? null,
            'assigned_to' => $_GET['assigned_to'] ?? null,
            'q'           => $_GET['q']           ?? null,
        ];
        $active = array_filter($filters);
        $rows = WorkOrder::listForTenant($tenantId, $active, 10000, 0, 'newest');

        $writer = new XLSXWriter();
        $writer->writeSheetHeader('Work Orders', [
            'Code'      => 'string',
            'Title'     => 'string',
            'Type'      => 'string',
            'Priority'  => 'string',
            'Status'    => 'string',
            'Asset'     => 'string',
            'Site'      => 'string',
            'Assigned'  => 'string',
            'Due Date'  => 'string',
            'Created'   => 'string',
            'Tasks'     => 'string',
        ]);

        foreach ($rows as $w) {
            $writer->writeSheetRow('Work Orders', [
                $w['code'] ?? '#' . $w['work_order_id'],
                $w['title'] ?? '',
                $w['wo_type'] ?? '',
                $w['priority'] ?? '',
                $w['status'] ?? '',
                $w['asset_name'] ?? '',
                $w['site_name'] ?? '',
                $w['assigned_username'] ?? '',
                $w['due_at'] ?? '',
                $w['created_at'] ?? '',
                ((int) ($w['task_done'] ?? 0)) . '/' . ((int) ($w['task_total'] ?? 0)),
            ]);
        }

        $filename = 'work_orders_' . date('Ymd_His') . '.xlsx';
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Cache-Control: max-age=0');
        $writer->writeToStdOut();
        exit;
    }

    public function pdf(int $id): void
    {
        $tenantId = $this->tenantId();
        $wo = WorkOrder::findInTenant($tenantId, $id);
        if (!$wo) { http_response_code(404); echo 'Not found'; return; }
        $tenant = Tenant::findById($tenantId);
        $tasks  = WorkOrder::tasksFor($id);
        ob_start();
        require ROOT . '/app/Views/pdf/work_order.php';
        $html = ob_get_clean();
        Pdf::stream($html, 'WO-' . ($wo['code'] ?: $id));
    }
}
