<?php

namespace App\Controllers;

use App\Libraries\FolioService;
use App\Models\AuditLogModel;
use App\Models\ClientModel;
use App\Models\InspectionAnswerModel;
use App\Models\InspectionAttachmentModel;
use App\Models\InspectionModel;
use App\Models\InspectionTemplateModel;
use App\Models\SignatureModel;
use CodeIgniter\HTTP\RedirectResponse;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Services;
use Mpdf\Mpdf;

class InspectionController extends BaseController
{
    public function index(): string
    {
        $rows = (new InspectionModel())
            ->select('inspections.*, clients.nombre AS client_name, inspection_templates.code AS template_code, inspection_templates.name AS template_name')
            ->join('clients', 'clients.id = inspections.client_id', 'left')
            ->join('inspection_templates', 'inspection_templates.id = inspections.template_id', 'left')
            ->orderBy('inspections.id', 'DESC')
            ->findAll();

        return view('inspections/index', [
            'title' => 'Inspecciones',
            'inspections' => $rows,
        ]);
    }

    public function create(): string
    {
        $templates = (new InspectionTemplateModel())
            ->where('activo', 1)
            ->orderBy('CAST(SUBSTRING(code, 4) AS UNSIGNED)', 'ASC', false)
            ->findAll();
        $clients = (new ClientModel())->orderBy('nombre')->findAll();

        return view('inspections/create', [
            'title' => 'Nueva inspección',
            'templates' => $templates,
            'clients' => $clients,
            'preselect_template' => (int) $this->request->getGet('template'),
            'preselect_client' => (int) $this->request->getGet('client'),
        ]);
    }

    public function store(): RedirectResponse
    {
        $data = [
            'template_id' => (int) $this->request->getPost('template_id'),
            'client_id' => (int) $this->request->getPost('client_id'),
            'sistema' => trim((string) $this->request->getPost('sistema')) ?: null,
            'equipo' => trim((string) $this->request->getPost('equipo')) ?: null,
            'atencion' => trim((string) $this->request->getPost('atencion')) ?: null,
            'servicio' => trim((string) $this->request->getPost('servicio')) ?: null,
            'contrato' => trim((string) $this->request->getPost('contrato')) ?: null,
            'fecha' => $this->request->getPost('fecha') ?: date('Y-m-d'),
            'periodicidad' => (new InspectionTemplateModel())->normalizePeriodicity((string) $this->request->getPost('periodicidad')),
            'inspector_user_id' => session('auth_user')['id'] ?? null,
            'status' => 'in_progress',
            'started_at' => date('Y-m-d H:i:s'),
            'folio' => (new FolioService())->next('inspection'),
            'verify_token' => FolioService::token(),
        ];

        if ($data['template_id'] === 0 || $data['client_id'] === 0) {
            return redirect()->back()->withInput()->with('error', 'Selecciona plantilla y cliente.');
        }

        $id = (new InspectionModel())->insert($data, true);
        (new AuditLogModel())->record('inspection', (int) $id, 'create', ['folio' => $data['folio']]);

        return redirect()->to(site_url('inspections/' . $id . '/edit'))->with('success', 'Inspección iniciada (folio ' . $data['folio'] . ').');
    }

    public function duplicate(int $id): RedirectResponse
    {
        $original = (new InspectionModel())->find($id);
        if ($original === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }

        $payload = $this->autosavePayloadForDuplicate($original);
        $newData = [
            'template_id' => (int) $original['template_id'],
            'client_id' => (int) $original['client_id'],
            'sistema' => $original['sistema'] ?? null,
            'equipo' => $original['equipo'] ?? null,
            'atencion' => $original['atencion'] ?? null,
            'servicio' => $original['servicio'] ?? null,
            'contrato' => $original['contrato'] ?? null,
            'fecha' => null,
            'periodicidad' => $original['periodicidad'] ?? 'mensual',
            'inspector_user_id' => session('auth_user')['id'] ?? null,
            'status' => 'in_progress',
            'observaciones' => $original['observaciones'] ?? null,
            'started_at' => date('Y-m-d H:i:s'),
            'folio' => (new FolioService())->next('inspection'),
            'verify_token' => FolioService::token(),
            'autosave_json' => json_encode($payload, JSON_UNESCAPED_UNICODE),
            'autosave_updated_at' => date('Y-m-d H:i:s'),
        ];

        $newId = (new InspectionModel())->insert($newData, true);
        (new AuditLogModel())->record('inspection', (int) $newId, 'duplicate', [
            'from_id' => $id,
            'from_folio' => $original['folio'] ?? null,
        ]);

        return redirect()
            ->to(site_url('inspections/' . $newId . '/edit'))
            ->with('success', 'Inspección duplicada con folio ' . $newData['folio'] . '.');
    }

    public function show(int $id): string
    {
        $inspection = (new InspectionModel())->find($id);
        if ($inspection === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }
        $template = $this->templateForInspection($inspection);
        $client = (new ClientModel())->find((int) $inspection['client_id']);
        $answers = (new InspectionAnswerModel())->getMapForInspection($id);
        $signature = (new SignatureModel())->findFor('inspection', $id, 'inspector');

        return view('inspections/show', [
            'title' => 'Inspección ' . ($inspection['folio'] ?? '#' . $id),
            'inspection' => $inspection,
            'template' => $template,
            'client' => $client,
            'answers' => $answers,
            'signature' => $signature,
        ]);
    }

    public function edit(int $id): string
    {
        $inspection = (new InspectionModel())->find($id);
        if ($inspection === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }
        $template = $this->templateForInspection($inspection);
        $client = (new ClientModel())->find((int) $inspection['client_id']);
        $answers = (new InspectionAnswerModel())->getMapForInspection($id);
        $signature = (new SignatureModel())->findFor('inspection', $id, 'inspector');

        return view('inspections/form', [
            'title' => 'Capturar inspección ' . ($inspection['folio'] ?? ''),
            'inspection' => $inspection,
            'template' => $template,
            'client' => $client,
            'answers' => $answers,
            'signature' => $signature,
        ]);
    }

    /**
     * Stores the autosave blob without validating individual fields.
     */
    public function autosave(int $id): ResponseInterface
    {
        $inspection = (new InspectionModel())->find($id);
        if ($inspection === null) {
            return $this->response->setStatusCode(404)->setJSON(['ok' => false]);
        }

        $payload = $this->request->getJSON(true);
        if (! is_array($payload)) {
            return $this->response->setStatusCode(400)->setJSON(['ok' => false]);
        }

        (new InspectionModel())->update($id, [
            'autosave_json' => json_encode($payload, JSON_UNESCAPED_UNICODE),
            'autosave_updated_at' => date('Y-m-d H:i:s'),
        ]);

        return $this->response->setJSON([
            'ok' => true,
            'savedAt' => date('c'),
            'csrf_token' => csrf_hash(),
        ]);
    }

    /**
     * Persists the autosave blob into inspection_answers and marks the inspection as signed.
     */
    public function finalize(int $id): RedirectResponse
    {
        $model = new InspectionModel();
        $inspection = $model->find($id);
        if ($inspection === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }

        $template = $this->templateForInspection($inspection);
        $data = json_decode((string) $inspection['autosave_json'], true);
        if (! is_array($data)) {
            $data = ['items' => [], 'observaciones' => '', 'header' => []];
        }

        $itemsById = [];
        foreach ($template['items'] as $it) {
            $itemsById[(int) $it['id']] = $it;
        }

        $missing = [];
        foreach ($template['items'] as $it) {
            if (! empty($it['obligatorio'])) {
                $val = $data['items'][$it['id']] ?? null;
                if (! is_array($val) || ! $this->itemHasValue($it, $val)) {
                    $missing[] = $it['numero'] . ' — ' . $it['pregunta'];
                }
            }
        }

        if ($missing !== []) {
            return redirect()->back()->with('errors', array_map(static fn ($m) => 'Falta: ' . $m, $missing));
        }

        $ansModel = new InspectionAnswerModel();
        $ansModel->where('inspection_id', $id)->delete();

        foreach (($data['items'] ?? []) as $itemId => $vals) {
            $itemId = (int) $itemId;
            if (! isset($itemsById[$itemId])) {
                continue;
            }
            $ansModel->insert([
                'inspection_id' => $id,
                'item_id' => $itemId,
                'value_bool' => isset($vals['value_bool']) && $vals['value_bool'] !== '' ? (int) $vals['value_bool'] : null,
                'value_text' => $vals['value_text'] ?? null,
                'value_number' => isset($vals['value_number']) && $vals['value_number'] !== '' ? (string) $vals['value_number'] : null,
                'value_json' => isset($vals['value_json']) ? json_encode($vals['value_json'], JSON_UNESCAPED_UNICODE) : null,
                'comentarios' => $vals['comentarios'] ?? null,
                'lecturas' => $vals['lecturas'] ?? null,
                'updated_at' => date('Y-m-d H:i:s'),
            ]);
        }

        $headerOverrides = $data['header'] ?? [];
        $update = [
            'observaciones' => $data['observaciones'] ?? null,
            'completed_at' => date('Y-m-d H:i:s'),
            'status' => 'signed',
            'signed_at' => date('Y-m-d H:i:s'),
            'autosave_json' => null,
        ];
        foreach (['sistema', 'equipo', 'atencion', 'servicio', 'contrato', 'fecha'] as $f) {
            if (isset($headerOverrides[$f]) && $headerOverrides[$f] !== '') {
                $update[$f] = $headerOverrides[$f];
            }
        }

        $model->update($id, $update);
        (new AuditLogModel())->record('inspection', $id, 'finalize', ['items' => count($data['items'] ?? [])]);

        return redirect()->to(site_url('inspections/' . $id))->with('success', 'Inspección finalizada y firmada.');
    }

    public function uploadAttachment(int $id): ResponseInterface
    {
        $inspection = (new InspectionModel())->find($id);
        if ($inspection === null) {
            return $this->response->setStatusCode(404)->setJSON(['ok' => false]);
        }

        $file = $this->request->getFile('file');
        if ($file === null || ! $file->isValid()) {
            return $this->response->setStatusCode(400)->setJSON(['ok' => false, 'msg' => 'Archivo inválido']);
        }

        $dir = WRITEPATH . 'uploads/inspections/' . $id;
        if (! is_dir($dir)) {
            mkdir($dir, 0775, true);
        }

        $name = $file->getRandomName();
        $file->move($dir, $name);

        $relPath = 'uploads/inspections/' . $id . '/' . $name;
        $attId = (new InspectionAttachmentModel())->insert([
            'inspection_id' => $id,
            'item_id' => $this->request->getPost('item_id') ? (int) $this->request->getPost('item_id') : null,
            'file_path' => $relPath,
            'mime' => $file->getMimeType(),
            'size' => filesize($dir . '/' . $name) ?: null,
        ], true);

        return $this->response->setJSON([
            'ok' => true,
            'id' => $attId,
            'path' => $relPath,
            'csrf_token' => csrf_hash(),
        ]);
    }

    public function delete(int $id): RedirectResponse
    {
        (new InspectionModel())->delete($id);
        (new AuditLogModel())->record('inspection', $id, 'delete');
        return redirect()->to(site_url('inspections'))->with('success', 'Inspección eliminada.');
    }

    public function pdf(int $id): ResponseInterface
    {
        return $this->renderPdf($id);
    }

    public function renderPdf(int $id, bool $forceDownload = false): ResponseInterface
    {
        $inspection = (new InspectionModel())->find($id);
        if ($inspection === null) {
            throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound();
        }
        $template = $this->templateForInspection($inspection);
        $client = (new ClientModel())->find((int) $inspection['client_id']);
        $answers = (new InspectionAnswerModel())->getMapForInspection($id);
        $signature = (new SignatureModel())->findFor('inspection', $id, 'inspector');

        $company = company_profile();
        $logoPath = company_brand_asset_path('logo');
        $verifyUrl = site_url('verify/' . $inspection['verify_token']);

        $view = $template['layout_mode'] === 'equipment_table' ? 'pdf/inspection_equipment' : 'pdf/inspection';
        $html = view($view, [
            'inspection' => $inspection,
            'template' => $template,
            'client' => $client,
            'answers' => $answers,
            'signature' => $signature,
            'company' => $company,
            'logoPath' => $logoPath,
            'verifyUrl' => $verifyUrl,
        ]);

        $mpdf = new Mpdf([
            'mode' => 'utf-8',
            'format' => 'Letter',
            'margin_left' => 10,
            'margin_right' => 10,
            'margin_top' => 12,
            'margin_bottom' => 14,
        ]);
        $mpdf->WriteHTML($html);

        $filename = ($inspection['folio'] ?? ('inspeccion-' . $id)) . '.pdf';
        $pdfBlob = $mpdf->Output($filename, 'S');

        $response = $this->response ?? service('response');

        return $response
            ->setHeader('Content-Type', 'application/pdf')
            ->setHeader('Content-Disposition', ($forceDownload ? 'attachment' : 'inline') . '; filename="' . $filename . '"')
            ->setBody($pdfBlob);
    }

    private function itemHasValue(array $item, array $val): bool
    {
        return match ($item['tipo_campo']) {
            'yes_no_na' => isset($val['value_bool']) && $val['value_bool'] !== '',
            'numeric' => isset($val['value_number']) && $val['value_number'] !== '',
            'checkbox', 'switch' => isset($val['value_bool']) && $val['value_bool'] !== '',
            'attachment' => ! empty($val['value_text']),
            'signature' => ! empty($val['value_text']),
            default => isset($val['value_text']) && trim((string) $val['value_text']) !== '',
        };
    }

    private function templateForInspection(array $inspection): array
    {
        $model = new InspectionTemplateModel();
        return $model->filterForPeriodicity(
            $model->findWithItems((int) $inspection['template_id']),
            $inspection['periodicidad'] ?? 'mensual'
        );
    }

    private function autosavePayloadForDuplicate(array $inspection): array
    {
        $payload = json_decode((string) ($inspection['autosave_json'] ?? ''), true);
        if (! is_array($payload)) {
            $payload = ['items' => [], 'observaciones' => $inspection['observaciones'] ?? '', 'header' => []];
            $answers = (new InspectionAnswerModel())->getMapForInspection((int) $inspection['id']);
            foreach ($answers as $itemId => $answer) {
                $payload['items'][$itemId] = [
                    'value_bool' => $answer['value_bool'],
                    'value_text' => $answer['value_text'],
                    'value_number' => $answer['value_number'],
                    'value_json' => $answer['value_json'] ? json_decode($answer['value_json'], true) : null,
                    'comentarios' => $answer['comentarios'],
                    'lecturas' => $answer['lecturas'],
                ];
            }
        }

        $payload['header'] = array_merge($payload['header'] ?? [], [
            'sistema' => $inspection['sistema'] ?? '',
            'equipo' => $inspection['equipo'] ?? '',
            'atencion' => $inspection['atencion'] ?? '',
            'servicio' => $inspection['servicio'] ?? '',
            'contrato' => $inspection['contrato'] ?? '',
            'fecha' => '',
        ]);
        $payload['observaciones'] = $payload['observaciones'] ?? ($inspection['observaciones'] ?? '');

        return $payload;
    }
}
