<?php
/**
 * POST /modules/turnero/api/create_solicitud.php
 * Crea la solicitud interna de un turno: vincula paciente, lugar y exámenes.
 * Además devuelve qué consentimientos serán necesarios (sin enviarlos aún).
 * Requiere login + módulo turnero.
 *
 * Body JSON:
 *   turno_id       int      requerido
 *   paciente_id    int      requerido
 *   lugar_id       int      requerido
 *   exam_tipo_ids  int[]    requerido  (al menos 1)
 *   total_cobrado  float    opcional
 *   metodo_pago    string   opcional
 *   observaciones  string   opcional
 */

require_once __DIR__ . '/_helpers.php';
requireMethod('POST');
requireTurnero();

$datos       = inputJson();
$turnoId     = isset($datos['turno_id'])    ? (int)   $datos['turno_id']    : 0;
$pacienteId  = isset($datos['paciente_id']) ? (int)   $datos['paciente_id'] : 0;
$lugarId     = isset($datos['lugar_id'])    ? (int)   $datos['lugar_id']    : 0;
$examIds     = isset($datos['exam_tipo_ids']) && is_array($datos['exam_tipo_ids'])
               ? array_filter(array_map('intval', $datos['exam_tipo_ids'])) : [];
$total       = isset($datos['total_cobrado']) && $datos['total_cobrado'] !== null
               ? (float) $datos['total_cobrado'] : null;
$metodoPago    = isset($datos['metodo_pago'])   ? trim((string) $datos['metodo_pago'])   : null;
$numeroRecibo  = !empty($datos['numero_recibo']) ? trim((string) $datos['numero_recibo']) : null;
$obs           = isset($datos['observaciones']) ? trim((string) $datos['observaciones']) : null;
$numOrden      = isset($datos['numero_orden'])  ? trim((string) $datos['numero_orden'])  : null;
$embarazada    = !empty($datos['embarazada']) ? 1 : 0;
$soloMuestras  = !empty($datos['solo_muestras']) ? 1 : 0;
$medicoId      = !empty($datos['medico_id'])    ? (int)$datos['medico_id']             : null;
$nitEmpresa    = !empty($datos['nit_empresa'])   ? trim((string)$datos['nit_empresa'])   : null;
$subgrupoId    = !empty($datos['subgrupo_id'])   ? (int)$datos['subgrupo_id']            : null;
$autorizacion  = !empty($datos['autorizacion'])  ? trim((string)$datos['autorizacion'])  : null;
$diagPpal      = !empty($datos['diag_ppal'])     ? trim((string)$datos['diag_ppal'])     : null;
$itemsPrecio   = isset($datos['items_precio']) && is_array($datos['items_precio'])
                 ? json_encode($datos['items_precio']) : null;
$pagosDetalle  = null;
$combRefs      = [];
if ($metodoPago === 'combinado' && isset($datos['pagos_detalle']) && is_array($datos['pagos_detalle'])) {
    $pd = $datos['pagos_detalle'];
    $pagosDetalle = json_encode([
        'efectivo'      => max(0, (float)($pd['efectivo']      ?? 0)),
        'transferencia' => max(0, (float)($pd['transferencia'] ?? 0)),
        'tarjeta'       => max(0, (float)($pd['tarjeta']       ?? 0)),
        'bono'          => max(0, (float)($pd['bono']          ?? 0)),
    ]);
    if (!empty($pd['ref_transferencia'])) $combRefs['transferencia'] = trim((string)$pd['ref_transferencia']);
    if (!empty($pd['ref_tarjeta']))       $combRefs['tarjeta']       = trim((string)$pd['ref_tarjeta']);
}
if ($numOrden === '') $numOrden = null;

// ── Validaciones ──────────────────────────────────────────────
if ($turnoId    <= 0) jsonError('turno_id inválido.');
if ($pacienteId <= 0) jsonError('paciente_id inválido.');
if ($lugarId    <= 0) jsonError('lugar_id inválido.');
if (empty($examIds) && !$soloMuestras) jsonError('Debe seleccionar al menos un examen.');

$metodosValidos = ['efectivo', 'transferencia', 'tarjeta', 'eps', 'cortesia', 'bono', 'combinado', ''];
if ($metodoPago && !in_array($metodoPago, $metodosValidos, true)) {
    jsonError('metodo_pago inválido.');
}

$pdo = db();

// Validar restricción de género antes de abrir transacción
if (!empty($examIds)) {
    $stPac = $pdo->prepare("SELECT genero FROM lab_pacientes WHERE id = ? LIMIT 1");
    $stPac->execute([$pacienteId]);
    $genPac = strtoupper(trim((string)($stPac->fetchColumn() ?: '')));

    if ($genPac) {
        $ph   = implode(',', array_fill(0, count($examIds), '?'));
        $stGp = $pdo->prepare(
            "SELECT codigo, nombre, genero_permitido FROM exam_tipos
              WHERE id IN ($ph) AND genero_permitido IS NOT NULL AND genero_permitido <> ?"
        );
        $stGp->execute(array_merge($examIds, [$genPac]));
        $incompatibles = $stGp->fetchAll(PDO::FETCH_ASSOC);
        if ($incompatibles) {
            $nombres = implode(', ', array_map(fn($r) => $r['codigo'], $incompatibles));
            $sexo    = $genPac === 'M' ? 'masculino' : 'femenino';
            jsonError("Examen(es) no disponibles para paciente $sexo: $nombres", 422);
        }
    }
}

$pdo->beginTransaction();

try {
    // Verificar que el turno existe y está en recepción
    $stmt = $pdo->prepare(
        "SELECT id, estado FROM turnero_turnos WHERE id = ? FOR UPDATE"
    );
    $stmt->execute([$turnoId]);
    $turno = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$turno) {
        $pdo->rollBack();
        jsonError('Turno no encontrado.', 404);
    }
    if (!in_array($turno['estado'], ['en_recepcion', 'espera'], true)) {
        $pdo->rollBack();
        jsonError("El turno está en estado '{$turno['estado']}' y no se puede editar su solicitud.", 422);
    }
    // Si el turno fue seleccionado sin llamarlo formalmente, promoverlo a en_recepcion ahora
    if ($turno['estado'] === 'espera') {
        $pdo->prepare(
            "UPDATE turnero_turnos
                SET estado = 'en_recepcion',
                    llamado_recepcion_at = COALESCE(llamado_recepcion_at, NOW()),
                    inicio_recepcion_at  = COALESCE(inicio_recepcion_at, NOW()),
                    atendido_recepcion_por = COALESCE(atendido_recepcion_por, ?)
              WHERE id = ?"
        )->execute([adminId(), $turnoId]);
    }

    // Verificar que el lugar existe
    $stmt = $pdo->prepare("SELECT id FROM turnero_lugares WHERE id = ? AND activo = 1");
    $stmt->execute([$lugarId]);
    if (!$stmt->fetch()) {
        $pdo->rollBack();
        jsonError('Lugar destino no encontrado o inactivo.', 404);
    }

    // Verificar que todos los exam_tipo_ids existen (se omite si solo_muestras y sin exámenes)
    if (!empty($examIds)) {
        $in = implode(',', array_fill(0, count($examIds), '?'));
        $stmt = $pdo->prepare("SELECT COUNT(*) FROM exam_tipos WHERE id IN ({$in}) AND activo = 1");
        $stmt->execute($examIds);
        if ((int)$stmt->fetchColumn() !== count($examIds)) {
            $pdo->rollBack();
            jsonError('Uno o más exámenes no existen o están inactivos.', 422);
        }
    }

    // ── Insertar o reemplazar solicitud (DELETE + INSERT para poder re-guardar) ──
    $stmt = $pdo->prepare("SELECT id FROM turnero_solicitudes WHERE turno_id = ?");
    $stmt->execute([$turnoId]);
    $solAnterior = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($solAnterior) {
        // Eliminar ítemes anteriores (CASCADE borrará los turnero_examen_items)
        $pdo->prepare("DELETE FROM turnero_solicitudes WHERE turno_id = ?")->execute([$turnoId]);
    }

    $stmt = $pdo->prepare(
        "INSERT INTO turnero_solicitudes
            (turno_id, paciente_id, lugar_id, numero_orden, total_cobrado, metodo_pago, numero_recibo, pagos_detalle,
             observaciones, embarazada, solo_muestras, medico_id,
             nit_empresa, subgrupo_id, autorizacion, diag_ppal, items_precio, creado_por)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
    );
    $stmt->execute([
        $turnoId, $pacienteId, $lugarId, $numOrden,
        $total, $metodoPago ?: null, $numeroRecibo, $pagosDetalle, $obs ?: null, $embarazada, $soloMuestras, $medicoId,
        $nitEmpresa, $subgrupoId, $autorizacion, $diagPpal, $itemsPrecio, adminId(),
    ]);
    $solicitudId = (int) $pdo->lastInsertId();

    // ── Insertar ítemes de examen ──────────────────────────────
    $stmtItem = $pdo->prepare(
        "INSERT INTO turnero_examen_items (solicitud_id, exam_tipo_id) VALUES (?, ?)"
    );
    foreach ($examIds as $examId) {
        $stmtItem->execute([$solicitudId, $examId]);
    }

    // ── Consentimientos: solo los de examen (los de lugar se crean al llegar a toma de muestras) ──
    $consentimientosRequeridos = [];
    if (!empty($examIds)) {
        $ph = implode(',', array_fill(0, count($examIds), '?'));
        $stmt = $pdo->prepare(
            "SELECT DISTINCT f.id AS formulario_id, f.nombre AS formulario_nombre
               FROM lab_formularios f
              INNER JOIN exam_tipo_consentimientos etc ON etc.formulario_id = f.id
              WHERE etc.exam_tipo_id IN ($ph)
                AND f.is_active = 1
              ORDER BY f.nombre ASC"
        );
        $stmt->execute($examIds);
        $consentimientosRequeridos = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $stmtCreateConsent = $pdo->prepare(
            "INSERT IGNORE INTO turnero_consentimientos (turno_id, formulario_id, token, estado, creado_por)
             VALUES (?, ?, UUID(), 'pendiente', ?)"
        );
        $stmtRead = $pdo->prepare(
            "SELECT id, token, estado FROM turnero_consentimientos
             WHERE turno_id = ? AND formulario_id = ?"
        );
        foreach ($consentimientosRequeridos as &$c) {
            $stmtCreateConsent->execute([$turnoId, $c['formulario_id'], adminId()]);
            $stmtRead->execute([$turnoId, $c['formulario_id']]);
            $row = $stmtRead->fetch(PDO::FETCH_ASSOC);
            $c['id']             = $row['id']     ?? null;
            $c['token']          = $row['token']  ?? null;
            $c['estado']         = $row['estado'] ?? 'pendiente';
            $c['turno_id']       = $turnoId;
            $c['origen_lugar_id'] = null; // es de examen, no de lugar
        }
        unset($c);
    }

    // ── Auto-crear muestras por tipo_muestra de los exámenes ──
    try {
        if (!$soloMuestras && !empty($examIds)) {
            $ph      = implode(',', array_fill(0, count($examIds), '?'));
            $stmtTm  = $pdo->prepare(
                "SELECT DISTINCT COALESCE(NULLIF(TRIM(tipo_muestra),''), codigo) AS tipo_key
                   FROM exam_tipos WHERE id IN ($ph) AND activo = 1"
            );
            $stmtTm->execute($examIds);
            $stmtIm = $pdo->prepare(
                "INSERT IGNORE INTO turnero_muestras (solicitud_id, tipo_muestra) VALUES (?, ?)"
            );
            foreach ($stmtTm->fetchAll(PDO::FETCH_COLUMN) as $tipoKey) {
                $stmtIm->execute([$solicitudId, $tipoKey]);
            }
        } elseif ($soloMuestras) {
            $pdo->prepare(
                "INSERT IGNORE INTO turnero_muestras (solicitud_id, tipo_muestra) VALUES (?, 'MUESTRA')"
            )->execute([$solicitudId]);
        }
    } catch (\Throwable $_) {
        // Tabla aún no existe (migración pendiente) — continuar sin muestras
    }

    // ── Crear lab_recepciones + lab_relaciones + lab_pagos ───────
    // Envuelto en try/catch para no romper el flujo si la migración 11
    // aún no se ha ejecutado en este entorno.
    $recepcionId = null;
    try {
        $operador = $_SESSION['admin_user']['username'] ?? ('u' . adminId());

        // Subgrupo nombre (para campo subgrupo en recepcion)
        $subgrupoNombre = null;
        if ($subgrupoId) {
            $stS = $pdo->prepare("SELECT subgrupo FROM lab_empresa_subgrupos WHERE id = ?");
            $stS->execute([$subgrupoId]);
            $subgrupoNombre = $stS->fetchColumn() ?: null;
        }

        // Tipo usuario de la empresa
        $tipoUsuario = null;
        if ($nitEmpresa) {
            $stE = $pdo->prepare("SELECT tipo_usuario FROM lab_empresas WHERE nit = ?");
            $stE->execute([$nitEmpresa]);
            $tipoUsuario = $stE->fetchColumn() ?: null;
        }

        // Siguiente num_factura para registros nuevos (es_historico=0)
        // FOR UPDATE garantiza secuencia correcta bajo concurrencia
        $nextFac = (int)$pdo->query(
            "SELECT COALESCE(MAX(num_factura), 0) + 1 FROM lab_recepciones WHERE es_historico = 0 FOR UPDATE"
        )->fetchColumn();

        // Calcular valor_desc desde el snapshot de precios
        $valorTotal  = $total ?? 0;
        $valorDesc   = 0;
        if ($itemsPrecio) {
            $snap      = json_decode($itemsPrecio, true) ?? [];
            $subtotalS = array_sum(array_column($snap, 'valor_final'));
            $valorDesc = max(0, round($subtotalS - $valorTotal, 2));
        }

        $stR = $pdo->prepare(
            "INSERT INTO lab_recepciones
                (solicitud_id, paciente_id, medico_id, nit_empresa, subgrupo,
                 fecha_recepcion, hora_inicio, num_factura,
                 valor_total, valor_desc, diag_ppal, tipo_usuario, autorizacion,
                 usuario, es_historico)
             VALUES (?, ?, ?, ?, ?, CURDATE(), CURTIME(), ?,
                     ?, ?, ?, ?, ?, ?, 0)"
        );
        $stR->execute([
            $solicitudId, $pacienteId, $medicoId, $nitEmpresa, $subgrupoNombre,
            $nextFac,
            $valorTotal, $valorDesc, $diagPpal, $tipoUsuario, $autorizacion,
            $operador,
        ]);
        $recepcionId = (int)$pdo->lastInsertId();

        // lab_relaciones: un registro por examen
        if (!empty($examIds)) {
            $ph    = implode(',', array_fill(0, count($examIds), '?'));
            $stEx  = $pdo->prepare("SELECT id, codigo FROM exam_tipos WHERE id IN ($ph)");
            $stEx->execute($examIds);
            $codigos = $stEx->fetchAll(PDO::FETCH_ASSOC);

            // Mapa exam_tipo_id → precio desde el snapshot
            $precioMap = [];
            if ($itemsPrecio) {
                foreach (json_decode($itemsPrecio, true) ?? [] as $item) {
                    $precioMap[(int)$item['exam_tipo_id']] = (float)($item['valor_final'] ?? 0);
                }
            }

            $stRel = $pdo->prepare(
                "INSERT INTO lab_relaciones (recepcion_id, cod_examen_legacy, exam_tipo_id, precio)
                 VALUES (?, ?, ?, ?)"
            );
            foreach ($codigos as $ex) {
                $stRel->execute([
                    $recepcionId,
                    $ex['codigo'],
                    (int)$ex['id'],
                    $precioMap[(int)$ex['id']] ?? 0,
                ]);
            }
        }

        // lab_pagos + lab_pagos_det si hay cobro
        if ($metodoPago && ($valorTotal > 0 || $metodoPago === 'cortesia')) {
            $stP = $pdo->prepare(
                "INSERT INTO lab_pagos (recepcion_id, valor, fecha, usuario)
                 VALUES (?, ?, CURDATE(), ?)"
            );
            $stP->execute([$recepcionId, $valorTotal, $operador]);
            $pagoId = (int)$pdo->lastInsertId();

            $stD = $pdo->prepare(
                "INSERT INTO lab_pagos_det (pago_id, tipo_pago, valor, num_doc) VALUES (?, ?, ?, ?)"
            );
            if ($metodoPago === 'combinado' && $pagosDetalle) {
                foreach (json_decode($pagosDetalle, true) ?? [] as $tipo => $val) {
                    if ((float)$val > 0) {
                        $ref = $combRefs[$tipo] ?? null;
                        $stD->execute([$pagoId, $tipo, (float)$val, $ref]);
                    }
                }
            } else {
                $stD->execute([$pagoId, $metodoPago, $valorTotal, $numeroRecibo]);
            }
        }
    } catch (\Throwable $eRec) {
        // Si falla (migración no aplicada, constraint, etc.) solo logueamos.
        // La solicitud principal ya está lista; no la revertimos por esto.
        error_log('[create_solicitud] lab_recepciones error: ' . $eRec->getMessage());
        $recepcionId = null;
    }

    $pdo->commit();
    notificarSSE(obtenerOCrearSesionHoy());

    // Recuperar solicitud completa
    $stmt = $pdo->prepare(
        "SELECT s.id, s.turno_id, s.paciente_id, s.lugar_id, s.total_cobrado,
                s.metodo_pago, s.observaciones, s.creado_at,
                p.nombre_completo AS paciente_nombre,
                p.numero_documento AS paciente_documento,
                l.nombre      AS lugar_nombre
           FROM turnero_solicitudes s
           LEFT JOIN lab_pacientes p ON p.id = s.paciente_id
           LEFT JOIN turnero_lugares l ON l.id = s.lugar_id
          WHERE s.id = ?"
    );
    $stmt->execute([$solicitudId]);
    $solicitud = $stmt->fetch(PDO::FETCH_ASSOC);

    // Bandeja WhatsApp — actualizar rips_examenes_pendientes (fuera de la transacción principal)
    if ($recepcionId && !empty($examIds) && ($solicitud['paciente_documento'] ?? '')) {
        try {
            $numDoc = $solicitud['paciente_documento'];
            $ph2    = implode(',', array_fill(0, count($examIds), '?'));
            $stEx2  = $pdo->prepare(
                "SELECT COALESCE(codigo_legacy, codigo) AS cod_rips FROM exam_tipos WHERE id IN ($ph2)"
            );
            $stEx2->execute($examIds);
            $codRips = array_column($stEx2->fetchAll(PDO::FETCH_ASSOC), 'cod_rips');

            $docidmedico = '';
            if ($medicoId) {
                $stM = $pdo->prepare("SELECT docidmedico FROM medicos WHERE id = ? LIMIT 1");
                $stM->execute([$medicoId]);
                $docidmedico = (string)($stM->fetchColumn() ?: '');
            }

            $meta  = [
                'medico_docidmedico' => $docidmedico,
                'nit_empresa'        => $nitEmpresa,
                'diagnostico_cod'    => $diagPpal,
                'valor_total'        => $valorTotal,
                'recepcion_id'       => $recepcionId,
                'hora'               => date('H:i'),
            ];
            $datos = array_map(fn($cod) => ['cod_examen' => $cod] + $meta, $codRips);

            // Consumir el cache: solo eliminar, no reinsertar, para que no recargue en una segunda visita
            $pdo->prepare(
                "DELETE FROM rips_examenes_pendientes WHERE numero_documento = ? AND DATE(created_at) = CURDATE()"
            )->execute([$numDoc]);

        } catch (\Throwable $eRips) {
            error_log('[create_solicitud] rips_examenes_pendientes: ' . $eRips->getMessage());
        }
    }

    jsonOk([
        'solicitud'                  => $solicitud,
        'recepcion_id'               => $recepcionId,
        'consentimientos_requeridos' => $consentimientosRequeridos,
    ], 'Solicitud guardada correctamente');

} catch (\Throwable $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();
    jsonError('Error al crear solicitud: ' . $e->getMessage(), 500);
}
