<?php
/**
 * Paciente — Gestión de pacientes del módulo administrativo de laboratorio.
 * Extiende la información del contacto WhatsApp (users.id).
 */

require_once __DIR__ . '/../../classes/Database.php';
require_once __DIR__ . '/ActividadAdmin.php';

// esBsuid() vive en config.php; se garantiza aquí por si esta clase se incluye
// directamente, sin pasar por el arranque del ERP.
if (!function_exists('esBsuid')) {
    $configPaciente = __DIR__ . '/../../config/config.php';
    if (file_exists($configPaciente)) {
        require_once $configPaciente;
    }
}

class Paciente {

    private Database $db;
    private ActividadAdmin $log;

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

    // ─────────────────────────────────────────────────────────────────────────
    // CRUD
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Lista paginada de pacientes con búsqueda.
     */
    public function listar(
        string $busqueda = '',
        int $pagina = 1,
        int $porPagina = 30,
        string $origen = ''
    ): array {
        $offset = ($pagina - 1) * $porPagina;
        $like   = "%$busqueda%";

        $conditions = [];
        $params     = [];

        if ($busqueda) {
            $conditions[] = "(p.nombre_completo LIKE ? OR p.numero_documento LIKE ? OR p.telefono LIKE ? OR p.email LIKE ?)";
            $params = [$like, $like, $like, $like];
        }
        if ($origen) {
            $conditions[] = "p.origen = ?";
            $params[] = $origen;
        }

        $where = $conditions ? 'WHERE ' . implode(' AND ', $conditions) : '';

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

        $rows = $this->db->fetchAll("
            SELECT
                p.*,
                u.phone_number,
                u.name AS whatsapp_name,
                (SELECT COUNT(*) FROM lab_ordenes_medicas o WHERE o.paciente_id = p.id) AS total_ordenes
            FROM  lab_pacientes p
            LEFT  JOIN users u ON u.id = p.user_id
            $where
            ORDER BY p.created_at DESC, p.nombre_completo ASC
            LIMIT ? OFFSET ?
        ", array_merge($params, [$porPagina, $offset]));

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

    public function stats(): array {
        return $this->db->fetch("
            SELECT
                COUNT(*) AS total,
                SUM(origen = 'lab')      AS importados,
                SUM(origen = 'manual')   AS manuales,
                SUM(origen = 'whatsapp') AS whatsapp,
                SUM(user_id IS NOT NULL) AS con_wa
            FROM lab_pacientes
            WHERE is_active = 1
        ") ?: ['total' => 0, 'importados' => 0, 'manuales' => 0, 'whatsapp' => 0, 'con_wa' => 0];
    }

    /**
     * Un paciente por ID (con datos del usuario WhatsApp).
     */
    public function obtener(int $id): ?array {
        return $this->db->fetch("
            SELECT
                p.*,
                u.phone_number,
                u.name      AS whatsapp_name,
                u.status    AS whatsapp_status,
                u.created_at AS whatsapp_desde
            FROM  lab_pacientes p
            LEFT  JOIN users u ON u.id = p.user_id
            WHERE p.id = ?
        ", [$id]) ?: null;
    }

    /**
     * Buscar por número de documento.
     */
    public function porDocumento(string $documento): ?array {
        return $this->db->fetch(
            'SELECT * FROM lab_pacientes WHERE numero_documento = ?',
            [$documento]
        ) ?: null;
    }

    /**
     * Buscar por user_id (teléfono WhatsApp).
     */
    public function porUserId(int $userId): ?array {
        return $this->db->fetch(
            'SELECT * FROM lab_pacientes WHERE user_id = ?',
            [$userId]
        ) ?: null;
    }

    /**
     * Crear paciente nuevo.
     *
     * @param array    $datos   Campos del formulario
     * @param int|null $adminId Quien lo crea (para trazabilidad)
     * @return int  ID del paciente creado
     */
    public function crear(array $datos, ?int $adminId = null): int {
        $campos = $this->filtrarCampos($datos);

        // Validar documento duplicado antes de insertar
        if (!empty($campos['numero_documento'])) {
            $existente = $this->db->fetch(
                'SELECT id, nombre_completo FROM lab_pacientes WHERE numero_documento = ?',
                [$campos['numero_documento']]
            );
            if ($existente) {
                throw new Exception(
                    'Ya existe un paciente registrado con ese número de documento: ' .
                    $existente['nombre_completo']
                );
            }
        }

        $id = $this->db->insert('lab_pacientes', $campos);
        $this->log->registrar($adminId, 'pacientes', 'crear', $id, $campos);
        return $id;
    }

    /**
     * Actualizar paciente existente.
     */
    public function actualizar(int $id, array $datos, ?int $adminId = null): bool {
        $campos = $this->filtrarCampos($datos);

        // Validar que el documento no pertenezca a otro paciente
        if (!empty($campos['numero_documento'])) {
            $existente = $this->db->fetch(
                'SELECT id, nombre_completo FROM lab_pacientes WHERE numero_documento = ? AND id != ?',
                [$campos['numero_documento'], $id]
            );
            if ($existente) {
                throw new Exception(
                    'Ese número de documento ya está registrado para: ' .
                    $existente['nombre_completo']
                );
            }
        }

        // Registrar antes y después de lo que realmente cambia. Guardar solo el
        // valor nuevo impide reconstruir el dato anterior si la corrección
        // resultó equivocada, que es justo cuando hace falta consultarlo.
        $antes  = $this->db->fetch('SELECT * FROM lab_pacientes WHERE id = ?', [$id]) ?: [];
        $cambios = [];
        foreach ($campos as $campo => $nuevo) {
            $previo = $antes[$campo] ?? null;
            if ((string)$previo !== (string)$nuevo) {
                $cambios[$campo] = ['antes' => $previo, 'despues' => $nuevo];
            }
        }

        $ok = $this->db->update('lab_pacientes', $campos, 'id = ?', [$id]);
        if ($cambios) {
            $this->log->registrar($adminId, 'pacientes', 'editar', $id, $cambios);
        }
        return $ok > 0;
    }

    /**
     * Desactivar (soft-delete).
     */
    public function desactivar(int $id, ?int $adminId = null): bool {
        $ok = $this->db->update('lab_pacientes', ['is_active' => 0], 'id = ?', [$id]);
        $this->log->registrar($adminId, 'pacientes', 'desactivar', $id);
        return $ok > 0;
    }

    /**
     * Vincular paciente con un usuario de WhatsApp.
     */
    public function vincularUsuario(int $pacienteId, int $userId, ?int $adminId = null): bool {
        $ok = $this->db->update('lab_pacientes', ['user_id' => $userId], 'id = ?', [$pacienteId]);
        $this->log->registrar($adminId, 'pacientes', 'vincular_whatsapp', $pacienteId, [
            'user_id' => $userId,
        ]);
        return $ok > 0;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Historial
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Órdenes médicas del paciente.
     */
    public function ordenes(int $pacienteId, int $limit = 20): array {
        return $this->db->fetchAll("
            SELECT
                o.*,
                au.full_name AS autorizada_por_nombre
            FROM  lab_ordenes_medicas o
            LEFT  JOIN admin_users au ON au.id = o.autorizada_por
            WHERE o.paciente_id = ?
            ORDER BY o.created_at DESC
            LIMIT ?
        ", [$pacienteId, $limit]);
    }

    /**
     * Historial de actividad de trazabilidad del paciente.
     */
    public function actividad(int $pacienteId): array {
        return $this->log->porEntidad('pacientes', $pacienteId);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // Helpers
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Crea automáticamente un paciente a partir de un users.id (whatsapp)
     * si aún no existe. Devuelve el id del paciente.
     */
    public function obtenerOCrearDesdeWhatsapp(int $userId): int {
        $existente = $this->porUserId($userId);
        if ($existente) {
            return $existente['id'];
        }

        $user = $this->db->fetch(
            'SELECT * FROM users WHERE id = ?',
            [$userId]
        );

        // Quien oculta su teléfono en WhatsApp se identifica con un BSUID, que ocupa
        // el lugar del número en `users`. No es un teléfono: guardarlo aquí dejaría
        // en la historia clínica un dato falso con apariencia de número real, porque
        // normalizarTelefono() le quita el punto y las letras y lo deja en 16 dígitos.
        // Mejor la ficha sin teléfono, que es la verdad: no lo tenemos.
        $identificador = $user['phone_number'] ?? null;
        $esIdentificadorSinTelefono = esBsuid($identificador);

        return $this->crear([
            'user_id'        => $userId,
            'nombre_completo'=> $user['name']
                ?? ($esIdentificadorSinTelefono ? 'Paciente sin identificar' : 'Paciente ' . $identificador),
            'telefono'       => $esIdentificadorSinTelefono ? null : $identificador,
        ]);
    }

    private function filtrarCampos(array $datos): array {
        $permitidos = [
            'user_id', 'numero_documento', 'tipo_documento',
            'nombre_completo', 'telefono', 'email',
            'fecha_nacimiento', 'genero', 'direccion',
            'ciudad', 'barrio', 'eps', 'notas_admin', 'is_active', 'origen',
        ];
        $campos = array_intersect_key($datos, array_flip($permitidos));
        if (!empty($campos['telefono'])) {
            $campos['telefono'] = $this->normalizarTelefono($campos['telefono']);
        }
        return $campos;
    }

    /**
     * Normaliza un número de teléfono colombiano al formato 57XXXXXXXXXX.
     * - Elimina espacios, guiones y paréntesis.
     * - Si empieza por +57 → quita el +.
     * - Si tiene 10 dígitos empezando por 3 → antepone 57.
     * - Cualquier otro caso lo devuelve limpio de caracteres no numéricos.
     */
    private function normalizarTelefono(string $tel): string {
        $tel = preg_replace('/[^0-9+]/', '', $tel);
        if (str_starts_with($tel, '+')) {
            $tel = ltrim($tel, '+');
        }
        // Sin prefijo (10 dígitos empezando por 3) → asume Colombia 57
        if (strlen($tel) === 10 && str_starts_with($tel, '3')) {
            $tel = '57' . $tel;
        }
        // Si ya trae prefijo de 2 dígitos (57xx... o 58xx...) lo respetamos
        return $tel;
    }
}
