<?php
/**
 * Formulario — Gestión de plantillas y envíos de formularios clínicos.
 */
require_once __DIR__ . '/../../classes/Database.php';
require_once __DIR__ . '/ActividadAdmin.php';

class Formulario {

    private Database $db;
    private ActividadAdmin $log;

    public function __construct() {
        $this->db  = Database::getInstance();
        $this->log = new ActividadAdmin();
    }

    // ─────────────────────────────────────────────────────────────────────────
    // PLANTILLAS (admin)
    // ─────────────────────────────────────────────────────────────────────────

    public function listar(bool $soloActivos = true): array {
        $w = $soloActivos ? 'WHERE f.is_active = 1' : '';
        return $this->db->fetchAll("
            SELECT f.*, u.full_name AS creado_por_nombre,
                   (SELECT COUNT(*) FROM lab_form_envios e WHERE e.formulario_id = f.id) +
                   (SELECT COUNT(*) FROM turnero_consentimientos tc WHERE tc.formulario_id = f.id) AS total_envios,
                   (SELECT COUNT(*) FROM lab_form_envios e WHERE e.formulario_id = f.id AND e.estado IN ('completado','firmado')) +
                   (SELECT COUNT(*) FROM turnero_consentimientos tc WHERE tc.formulario_id = f.id AND tc.estado = 'firmado') AS total_completados
            FROM lab_formularios f
            LEFT JOIN admin_users u ON u.id = f.creado_por
            $w
            ORDER BY f.created_at DESC
        ");
    }

    public function obtener(int $id): ?array {
        $row = $this->db->fetch(
            "SELECT f.*, u.full_name AS creado_por_nombre
             FROM lab_formularios f
             LEFT JOIN admin_users u ON u.id = f.creado_por
             WHERE f.id = ?",
            [$id]
        );
        if ($row) {
            $row['esquema_decoded'] = json_decode($row['esquema'], true) ?? [];
        }
        return $row ?: null;
    }

    public function crear(array $datos, int $adminId): int {
        $this->validarEsquema($datos['esquema'] ?? '[]');
        $id = $this->db->insert('lab_formularios', [
            'nombre'          => trim($datos['nombre']),
            'descripcion'     => $datos['descripcion']     ?? null,
            'categoria'       => $datos['categoria']       ?? 'otro',
            'esquema'         => $datos['esquema']         ?? '[]',
            'permite_firma'   => !empty($datos['permite_firma'])   ? 1 : 0,
            'requiere_firma'  => !empty($datos['requiere_firma'])  ? 1 : 0,
            'firma_modos'     => $datos['firma_modos']     ?? 'canvas,foto',
            'doc_encabezado'  => $datos['doc_encabezado']  ?? null,
            'doc_subtitulo'   => $datos['doc_subtitulo']   ?? null,
            'doc_logo_base64' => $datos['doc_logo_base64'] ?? null,
            'doc_color'       => $datos['doc_color']       ?? null,
            'doc_pie_pagina'  => $datos['doc_pie_pagina']  ?? null,
            'creado_por'      => $adminId,
        ]);
        $this->log->registrar($adminId, 'formularios', 'crear', $id, ['nombre' => $datos['nombre']]);
        return $id;
    }

    public function actualizar(int $id, array $datos, int $adminId): bool {
        $permitidos = [
            'nombre','descripcion','categoria','esquema',
            'permite_firma','requiere_firma','firma_modos','is_active',
            'doc_encabezado','doc_subtitulo','doc_logo_base64','doc_color','doc_pie_pagina',
        ];
        $campos = array_intersect_key($datos, array_flip($permitidos));
        if (isset($campos['esquema'])) {
            $this->validarEsquema($campos['esquema']);
            $row = $this->db->fetch('SELECT version FROM lab_formularios WHERE id = ?', [$id]);
            $campos['version'] = (int)($row['version'] ?? 0) + 1;
        }
        $ok = $this->db->update('lab_formularios', $campos, 'id = ?', [$id]);
        $this->log->registrar($adminId, 'formularios', 'editar', $id, $campos);
        // rowCount() = 0 cuando los datos son idénticos a los guardados (no es un error)
        return true;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // ENVÍOS
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Crear un envío con datos pre-llenados, genera token único.
     */
    public function crearEnvio(array $datos, int $enviadoPor): int {
        $token = bin2hex(random_bytes(32));
        $expira = null; // El enlace no expira

        $id = $this->db->insert('lab_form_envios', [
            'formulario_id'   => (int)$datos['formulario_id'],
            'paciente_id'     => !empty($datos['paciente_id'])   ? (int)$datos['paciente_id']  : null,
            'domicilio_id'    => !empty($datos['domicilio_id'])  ? (int)$datos['domicilio_id'] : null,
            'token'           => $token,
            'datos_prefilled' => $datos['datos_prefilled'] ?? '{}',
            'estado'          => 'pendiente',
            'enviado_por'     => $enviadoPor,
            'enviado_via'     => $datos['enviado_via']    ?? 'whatsapp',
            'expira_en'       => $expira,
        ]);
        $this->log->registrar($enviadoPor, 'formularios', 'enviar', $id, [
            'formulario_id' => $datos['formulario_id'],
            'via'           => $datos['enviado_via'] ?? 'whatsapp',
        ]);
        return $id;
    }

    /**
     * Obtener envío por token (uso público del cliente).
     */
    public function obtenerPorToken(string $token): ?array {
        $envio = $this->db->fetch(
            "SELECT e.*, f.nombre AS form_nombre, f.esquema, f.permite_firma, f.requiere_firma, f.descripcion AS form_descripcion,
                    f.doc_color, f.doc_logo_base64, f.doc_encabezado, f.doc_subtitulo, f.doc_pie_pagina, f.firma_modos,
                    p.nombre_completo AS paciente_nombre, p.numero_documento, p.telefono AS paciente_telefono
             FROM lab_form_envios e
             JOIN lab_formularios f ON f.id = e.formulario_id
             LEFT JOIN lab_pacientes p ON p.id = e.paciente_id
             WHERE e.token = ?",
            [$token]
        );
        if (!$envio) return null;
        if ($envio['estado'] === 'expirado') return null;
        if ($envio['expira_en'] && strtotime($envio['expira_en']) < time()) {
            $this->db->update('lab_form_envios', ['estado' => 'expirado'], 'id = ?', [$envio['id']]);
            return null;
        }
        $envio['esquema_decoded']   = json_decode($envio['esquema'],         true) ?? [];
        $envio['prefilled_decoded'] = json_decode($envio['datos_prefilled'], true) ?? [];
        return $envio;
    }

    /**
     * Guardar respuesta del cliente (firma + datos).
     */
    public function guardarRespuesta(string $token, array $datosCliente, ?string $firmaSvg, string $ip, string $ua): bool {
        $envio = $this->db->fetch('SELECT id, estado FROM lab_form_envios WHERE token = ?', [$token]);
        if (!$envio || $envio['estado'] === 'expirado') return false;

        $tieneCanvas = !empty($firmaSvg);
        $tieneFoto   = !empty($datosCliente['__firma_foto']);
        $estado = ($tieneCanvas || $tieneFoto) ? 'firmado' : 'completado';

        // Hash de verificación SHA-256 (vincula contenido + firma + timestamp)
        $payloadHash = json_encode($datosCliente, JSON_UNESCAPED_UNICODE)
                     . ($firmaSvg ?? '')
                     . $envio['id']
                     . $envio['token']
                     . date('Y-m-d H:i:s');
        $hash = hash('sha256', $payloadHash);

        $this->db->update('lab_form_envios', [
            'datos_cliente'      => json_encode($datosCliente, JSON_UNESCAPED_UNICODE),
            'firma_svg'          => $firmaSvg,
            'hash_verificacion'  => $hash,
            'ip_cliente'         => $ip,
            'user_agent'         => substr($ua, 0, 512),
            'estado'             => $estado,
            'completado_en'      => date('Y-m-d H:i:s'),
        ], 'token = ?', [$token]);
        return true;
    }

    /**
     * Listar envíos (admin/enfermero).
     */
    public function listarEnvios(array $filtros = [], int $pagina = 1, int $porPagina = 20): array {
        $origen = $filtros['origen'] ?? ''; // 'turnero' | 'otros' | '' (todos)
        $offset = ($pagina - 1) * $porPagina;

        // ── Rama 1: envíos normales (domicilios, envío manual, etc.) ──────────
        $whereA  = ['1=1'];
        $paramsA = [];
        if (!empty($filtros['formulario_id'])) { $whereA[] = 'e.formulario_id = ?'; $paramsA[] = $filtros['formulario_id']; }
        if (!empty($filtros['paciente_id']))    { $whereA[] = 'e.paciente_id = ?';   $paramsA[] = $filtros['paciente_id']; }
        if (!empty($filtros['enviado_por']))    { $whereA[] = 'e.enviado_por = ?';   $paramsA[] = $filtros['enviado_por']; }
        if (!empty($filtros['estado']))         { $whereA[] = 'e.estado = ?';        $paramsA[] = $filtros['estado']; }
        if (!empty($filtros['fecha_desde']))    { $whereA[] = 'DATE(e.created_at) >= ?'; $paramsA[] = $filtros['fecha_desde']; }
        if (!empty($filtros['fecha_hasta']))    { $whereA[] = 'DATE(e.created_at) <= ?'; $paramsA[] = $filtros['fecha_hasta']; }
        if (!empty($filtros['paciente']))       { $whereA[] = 'p.nombre_completo LIKE ?'; $paramsA[] = '%' . $filtros['paciente'] . '%'; }
        $wA = implode(' AND ', $whereA);

        $sqlA = "SELECT e.id, e.formulario_id, e.estado, e.created_at AS fecha,
                        f.nombre AS form_nombre, f.categoria,
                        p.nombre_completo AS paciente_nombre,
                        u.full_name AS enviado_por_nombre,
                        'otros' AS origen, e.token, NULL AS turno_codigo
                 FROM   lab_form_envios e
                 JOIN   lab_formularios f ON f.id = e.formulario_id
                 LEFT   JOIN lab_pacientes p ON p.id = e.paciente_id
                 LEFT   JOIN admin_users   u ON u.id = e.enviado_por
                 WHERE  $wA";

        // ── Rama 2: consentimientos del turnero ───────────────────────────────
        $whereB  = ['1=1'];
        $paramsB = [];
        if (!empty($filtros['formulario_id'])) { $whereB[] = 'tc.formulario_id = ?'; $paramsB[] = $filtros['formulario_id']; }
        if (!empty($filtros['estado'])) {
            // Mapear estados: firmado=firmado, pendiente=pendiente/enviado/visto
            if ($filtros['estado'] === 'firmado') {
                $whereB[] = "tc.estado = 'firmado'";
            } elseif ($filtros['estado'] === 'pendiente') {
                $whereB[] = "tc.estado IN ('pendiente','enviado','visto')";
            } else {
                $whereB[] = '1=0'; // estados como 'completado'/'expirado' no aplican al turnero
            }
        }
        if (!empty($filtros['fecha_desde']))    { $whereB[] = 'DATE(COALESCE(tc.firmado_at, tc.enviado_at, t.creado_at)) >= ?';  $paramsB[] = $filtros['fecha_desde']; }
        if (!empty($filtros['fecha_hasta']))    { $whereB[] = 'DATE(COALESCE(tc.firmado_at, tc.enviado_at, t.creado_at)) <= ?';  $paramsB[] = $filtros['fecha_hasta']; }
        if (!empty($filtros['paciente']))       { $whereB[] = 'p.nombre_completo LIKE ?';  $paramsB[] = '%' . $filtros['paciente'] . '%'; }
        if (!empty($filtros['enviado_por']))    { $whereB[] = '1=0'; } // turnero no tiene enviado_por
        $wB = implode(' AND ', $whereB);

        $sqlB = "SELECT tc.id, tc.formulario_id, tc.estado, COALESCE(tc.firmado_at, tc.enviado_at, t.creado_at) AS fecha,
                        f.nombre AS form_nombre, f.categoria,
                        p.nombre_completo AS paciente_nombre,
                        'Turnero' AS enviado_por_nombre,
                        'turnero' AS origen, tc.token, t.codigo AS turno_codigo
                 FROM   turnero_consentimientos tc
                 JOIN   lab_formularios f ON f.id = tc.formulario_id
                 JOIN   turnero_turnos t ON t.id = tc.turno_id
                 LEFT   JOIN turnero_solicitudes ts ON ts.turno_id = tc.turno_id
                 LEFT   JOIN lab_pacientes p ON p.id = ts.paciente_id
                 WHERE  $wB";

        // ── Combinar según filtro origen ──────────────────────────────────────
        if ($origen === 'turnero') {
            $sql    = $sqlB;
            $params = $paramsB;
        } elseif ($origen === 'otros') {
            $sql    = $sqlA;
            $params = $paramsA;
        } else {
            $sql    = "($sqlA) UNION ALL ($sqlB)";
            $params = array_merge($paramsA, $paramsB);
        }

        $total = $this->db->fetch(
            "SELECT COUNT(*) AS n FROM ($sql) AS _u", $params
        )['n'] ?? 0;

        $rows = $this->db->fetchAll(
            "SELECT * FROM ($sql) AS _u ORDER BY fecha DESC LIMIT ? OFFSET ?",
            array_merge($params, [$porPagina, $offset])
        );

        return [
            'data'       => $rows,
            'total'      => (int)$total,
            'pagina'     => $pagina,
            'por_pagina' => $porPagina,
            'paginas'    => (int)ceil($total / max(1, $porPagina)),
        ];
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Helper
    // ─────────────────────────────────────────────────────────────────────────
    private function validarEsquema(string $json): void {
        $decoded = json_decode($json, true);
        if (!is_array($decoded)) {
            throw new InvalidArgumentException('El esquema del formulario no es un JSON válido');
        }
    }
}
