<?php
require_once __DIR__ . '/config.php';
requireLicense();
$user = requireAuth();

$entity = strParam('entity');
$id     = intParam('id');
$method = $_SERVER['REQUEST_METHOD'];

$entityMap = [
    'patients'          => ['table'=>'patients',          'perm'=>'patients'],
    'appointments'      => ['table'=>'appointments',      'perm'=>'appointments'],
    'attendances'       => ['table'=>'attendances',       'perm'=>'payments'],
    'photos'            => ['table'=>'photos',            'perm'=>'photos'],
    'laboratory'        => ['table'=>'laboratory',        'perm'=>'laboratory'],
    'inventory'         => ['table'=>'inventory',         'perm'=>'inventory'],
    'suppliers'         => ['table'=>'suppliers',         'perm'=>'suppliers'],
    'services'          => ['table'=>'services',          'perm'=>'config'],
    'categories'        => ['table'=>'categories',        'perm'=>'config'],
    'sales_products'    => ['table'=>'sales_products',    'perm'=>'ventas'],
    'sales_services'    => ['table'=>'sales_services',    'perm'=>'ventas'],
    'sales_orders'      => ['table'=>'sales_orders',      'perm'=>'payments'],
    'payments'          => ['table'=>'payments',          'perm'=>'payments'],
    'memberships'       => ['table'=>'memberships',       'perm'=>'patients'],
    'users'             => ['table'=>'users',             'perm'=>'admin'],
    'reminders'         => ['table'=>'reminders',         'perm'=>'reminders'],
    'cashbox_cuts'      => ['table'=>'cashbox_cuts',      'perm'=>'corte'],
    'kpi_settings'      => ['table'=>'kpi_settings',      'perm'=>'admin'],
    'worker_goals'      => ['table'=>'worker_goals',      'perm'=>'admin'],
];

if (!isset($entityMap[$entity])) jsonError("Entidad no válida: $entity");
$cfg   = $entityMap[$entity];
$table = $cfg['table'];
$perm  = $cfg['perm'];

$isWrite = in_array($method, ['POST','PUT','DELETE','PATCH']);
$readFreeEntities = ['inventory','suppliers','categories','services','sales_products','sales_services','users'];
$readFreeExempt = in_array($entity, $readFreeEntities) && !$isWrite;
if (!$readFreeExempt) {
    $cashboxEntities = ['attendances', 'sales_orders'];
    if (in_array($entity, $cashboxEntities, true)) {
        if (!can($user, 'payments') && !can($user, 'cashbox') && !can($user, 'ventas')) {
            jsonError('Sin permisos para esta acciÃ³n', 403);
        }
    } else {
        requirePerm($user, $perm);
    }
}

$db = getDB();

$jsonFields = ['pathologies','path_ant','zone_left','zone_right','nails_l','nails_r',
               'nail_color','nail_thick','nail_shape',
               'photos','items','by_method','perms','podo_functions','kpis',
               'podoFunctions','order_items','moves','custom_pathologies',
               'custom_antecedents','custom_tags'];

function encodeJsonFields(array &$data): void {
    global $jsonFields;
    foreach ($jsonFields as $jf) {
        if (isset($data[$jf]) && is_array($data[$jf])) {
            $data[$jf] = json_encode($data[$jf], JSON_UNESCAPED_UNICODE);
        }
    }
}
function decodeJsonFields(array &$row): void {
    global $jsonFields;
    foreach ($jsonFields as $jf) {
        if (isset($row[$jf]) && is_string($row[$jf]) && strlen($row[$jf]) > 0) {
            $dec = json_decode($row[$jf], true);
            if ($dec !== null) $row[$jf] = $dec;
        }
    }
}

function normalizePatientData(array &$data): void {
    $map = [
        'registrationDate'  => 'registration_date',
        'firstName'         => 'first_name',
        'lastName'          => 'last_name',
        'pathAnt'           => 'path_ant',
        'zoneLeft'          => 'zone_left',
        'zoneRight'         => 'zone_right',
        'nailColor'         => 'nail_color',
        'nailThickness'     => 'nail_thick',
        'nailShape'         => 'nail_shape',
        'shoeSize'          => 'shoe_size',
        'shoeType'          => 'shoe_type',
        'patientType'       => 'patient_type',
        'emergencyContact'  => 'emergency_contact',
        'emergencyPhone'    => 'emergency_phone',
        'attendedBy'        => 'attended_by',
        'physicalActivity'  => 'physical_activity',
        'activityDetail'    => 'activity_detail',
        'sharesShoes'       => 'shares_shoes',
        'membershipId'      => 'membership_id',
        'convenioName'      => 'convenio_name',
        'convenioPrice'     => 'convenio_price',
        'referredBy'        => 'referred_by',
        'customPathologies' => 'custom_pathologies',
        'customAntecedents' => 'custom_antecedents',
        'customTags'        => 'custom_tags',
    ];
    foreach ($map as $from => $to) {
        if (array_key_exists($from, $data) && !array_key_exists($to, $data)) {
            $data[$to] = $data[$from];
        }
        unset($data[$from]);
    }
    foreach (['weight','height','convenio_price'] as $num) {
        if (array_key_exists($num, $data) && $data[$num] === '') $data[$num] = null;
    }
    foreach (['registration_date','dob'] as $date) {
        if (array_key_exists($date, $data) && $data[$date] === '') $data[$date] = null;
    }
}

function normalizeAppointmentData(array &$data): void {
    $map = [
        'patientId' => 'patient_id',
        'createdBy' => 'created_by',
        'rescheduledAt' => 'rescheduled_at',
    ];
    foreach ($map as $from => $to) {
        if (array_key_exists($from, $data) && !array_key_exists($to, $data)) {
            $data[$to] = $data[$from];
        }
        unset($data[$from]);
    }
    if (isset($data['status'])) {
        $s = strtolower(trim((string)$data['status']));
        $statusMap = [
            'pendiente' => 'pending',
            'confirmado' => 'confirmed',
            'confirmada' => 'confirmed',
            'atendida' => 'done',
            'atendido' => 'done',
            'cancelado' => 'cancelled',
            'cancelada' => 'cancelled',
        ];
        $data['status'] = $statusMap[$s] ?? $s;
    }
    foreach (['date'] as $date) {
        if (array_key_exists($date, $data) && $data[$date] === '') $data[$date] = null;
    }
}

function normalizeAttendanceData(array &$data): void {
    $map = [
        'patientId'     => 'patient_id',
        'patientName'   => 'patient_name',
        'patientDni'    => 'patient_dni',
        'attendedBy'    => 'attended_by',
        'payMethod'     => 'pay_method',
        'paidMethod'    => 'pay_method',
        'payStatus'     => 'pay_status',
        'registeredAt'  => 'registered_at',
        'appointmentId' => 'appointment_id',
        'itemType'      => 'item_type',
        'orderItems'    => 'order_items',
        'orderCode'     => 'order_code',
        'orderLineKey'  => 'order_line_key',
        'grossPrice'    => 'gross_price',
    ];
    foreach ($map as $from => $to) {
        if (array_key_exists($from, $data) && !array_key_exists($to, $data)) {
            $data[$to] = $data[$from];
        }
        unset($data[$from]);
    }
    if (isset($data['pay_method'])) {
        $method = trim((string)$data['pay_method']);
        $data['pay_method'] = $method !== '' ? $method : 'Efectivo';
    }
    if (isset($data['pay_status'])) {
        $data['pay_status'] = strtolower(trim((string)$data['pay_status']));
    }
}

function ensurePatientSchema(PDO $db): void {
    static $done = false;
    if ($done) return;
    $defs = [
        'registration_date'  => "DATE DEFAULT NULL",
        'first_name'         => "VARCHAR(150) DEFAULT NULL",
        'last_name'          => "VARCHAR(150) DEFAULT NULL",
        'dni'                => "VARCHAR(20) DEFAULT NULL",
        'dob'                => "DATE DEFAULT NULL",
        'sex'                => "CHAR(1) DEFAULT NULL",
        'phone'              => "VARCHAR(50) DEFAULT NULL",
        'email'              => "VARCHAR(150) DEFAULT NULL",
        'address'            => "VARCHAR(300) DEFAULT NULL",
        'occupation'         => "VARCHAR(150) DEFAULT NULL",
        'weight'             => "DECIMAL(6,2) DEFAULT NULL",
        'height'             => "DECIMAL(6,2) DEFAULT NULL",
        'shoe_size'          => "VARCHAR(10) DEFAULT NULL",
        'shoe_type'          => "VARCHAR(100) DEFAULT NULL",
        'pathologies'        => "TEXT DEFAULT NULL",
        'path_ant'           => "TEXT DEFAULT NULL",
        'location'           => "TEXT DEFAULT NULL",
        'triggers'           => "TEXT DEFAULT NULL",
        'smoke'              => "VARCHAR(10) DEFAULT NULL",
        'alcohol'            => "VARCHAR(10) DEFAULT NULL",
        'meds'               => "TEXT DEFAULT NULL",
        'shares_shoes'       => "VARCHAR(10) DEFAULT NULL",
        'physical_activity'  => "VARCHAR(80) DEFAULT NULL",
        'activity_detail'    => "VARCHAR(200) DEFAULT NULL",
        'zone_left'          => "TEXT DEFAULT NULL",
        'zone_right'         => "TEXT DEFAULT NULL",
        'nail_color'         => "TEXT DEFAULT NULL",
        'nail_thick'         => "TEXT DEFAULT NULL",
        'nail_shape'         => "TEXT DEFAULT NULL",
        'notes'              => "TEXT DEFAULT NULL",
        'status'             => "VARCHAR(20) DEFAULT 'active'",
        'patient_type'       => "VARCHAR(30) DEFAULT NULL",
        'membership_id'      => "INT DEFAULT NULL",
        'convenio_name'      => "VARCHAR(150) DEFAULT NULL",
        'convenio_price'     => "DECIMAL(10,2) DEFAULT NULL",
        'emergency_contact'  => "VARCHAR(150) DEFAULT NULL",
        'emergency_phone'    => "VARCHAR(50) DEFAULT NULL",
        'insurance'          => "VARCHAR(150) DEFAULT NULL",
        'referred_by'        => "VARCHAR(150) DEFAULT NULL",
        'attended_by'        => "VARCHAR(150) DEFAULT NULL",
        'custom_pathologies' => "TEXT DEFAULT NULL",
        'custom_antecedents' => "TEXT DEFAULT NULL",
        'custom_tags'        => "TEXT DEFAULT NULL",
    ];
    $cols = array_column($db->query("DESCRIBE `patients`")->fetchAll(), 'Field');
    foreach ($defs as $col => $def) {
        if (!in_array($col, $cols, true)) {
            $db->exec("ALTER TABLE `patients` ADD COLUMN `$col` $def");
        }
    }
    $done = true;
}

function buildWhere(string $entity, array $params): array {
    $where = []; $vals = [];
    if ($entity === 'patients') {
        $q = $params['q'] ?? $params['search'] ?? '';
        $dniExact = $params['dni'] ?? '';
        if ($dniExact) { $where[] = "dni=?"; $vals[] = $dniExact; }
        elseif ($q) {
            $like = "%$q%";
            $where[] = "(first_name LIKE ? OR last_name LIKE ? OR dni LIKE ? OR CONCAT(first_name,' ',last_name) LIKE ?)";
            $vals = array_merge($vals, [$like,$like,$like,$like]);
        }
    }
    if ($entity === 'appointments') {
        $date = $params['date'] ?? ''; $from = $params['from'] ?? ''; $to = $params['to'] ?? '';
        if ($date) { $where[] = "date=?"; $vals[] = $date; }
        elseif ($from && $to) { $where[] = "date BETWEEN ? AND ?"; $vals[] = $from; $vals[] = $to; }
        $status = $params['status'] ?? '';
        if ($status) { $where[] = "status=?"; $vals[] = $status; }
    }
    if ($entity === 'attendances') {
        $date = $params['date'] ?? ''; $from = $params['from'] ?? ''; $to = $params['to'] ?? '';
        $pid = $params['patient_id'] ?? '';
        if ($date) { $where[] = "date=?"; $vals[] = $date; }
        elseif ($from && $to) { $where[] = "date BETWEEN ? AND ?"; $vals[] = $from; $vals[] = $to; }
        if ($pid) { $where[] = "patient_id=?"; $vals[] = (int)$pid; }
    }
    if ($entity === 'sales_orders') {
        $from = $params['from'] ?? ''; $to = $params['to'] ?? '';
        if ($from && $to) { $where[] = "date BETWEEN ? AND ?"; $vals[] = $from; $vals[] = $to; }
    }
    return [$where, $vals];
}

// ─── GET ───────────────────────────────────────────────────────────────────────
if ($method === 'GET') {
    if ($id) {
        if ($entity === 'attendances') {
            $st = $db->prepare(
                "SELECT a.*, p.first_name, p.last_name, p.dni AS patient_dni,
                        COALESCE(NULLIF(TRIM(CONCAT(COALESCE(p.first_name,''),' ',COALESCE(p.last_name,''))),''), a.patient_name) AS patient_name
                 FROM attendances a
                 LEFT JOIN patients p ON a.patient_id=p.id OR a.patient_id=p.dni
                 WHERE a.id=? LIMIT 1"
            );
        } else {
            $st = $db->prepare("SELECT * FROM `$table` WHERE id=? LIMIT 1");
        }
        $st->execute([$id]);
        $row = $st->fetch();
        if (!$row) jsonError('No encontrado', 404);
        decodeJsonFields($row);
        if ($entity === 'users') { unset($row['password_hash']); $row['podoFunctions'] = $row['podo_functions'] ?? []; }
        jsonOk($row);
    }
    [$where, $vals] = buildWhere($entity, $_GET);
    $whereStr = $where ? 'WHERE ' . implode(' AND ', $where) : '';
    $limit  = min((int)($_GET['limit'] ?? 500), 2000);
    $offset = intParam('offset', 0);
    $orderMap = [
        'patients'    => 'last_name ASC, first_name ASC',
        'appointments'=> 'date DESC, time ASC',
        'attendances' => 'date DESC, id DESC',
        'photos'      => 'date DESC',
        'laboratory'  => 'date DESC',
        'sales_orders'=> 'date DESC, id DESC',
    ];
    $order = $orderMap[$entity] ?? 'id DESC';
    if ($entity === 'attendances') {
        $st = $db->prepare(
            "SELECT a.*, p.first_name, p.last_name, p.dni AS patient_dni,
                    COALESCE(NULLIF(TRIM(CONCAT(COALESCE(p.first_name,''),' ',COALESCE(p.last_name,''))),''), a.patient_name) AS patient_name
             FROM attendances a
             LEFT JOIN patients p ON a.patient_id=p.id OR a.patient_id=p.dni
             $whereStr ORDER BY a.date DESC, a.id DESC LIMIT $limit OFFSET $offset"
        );
    } else {
        $st = $db->prepare("SELECT * FROM `$table` $whereStr ORDER BY $order LIMIT $limit OFFSET $offset");
    }
    $st->execute($vals);
    $rows = $st->fetchAll();
    foreach ($rows as &$row) {
        decodeJsonFields($row);
        if ($entity === 'users') {
            unset($row['password_hash']);
            $row['podoFunctions'] = $row['podo_functions'] ?? [];
        }
    }
    unset($row);
    $cntSt = $db->prepare("SELECT COUNT(*) FROM `$table` $whereStr");
    $cntSt->execute($vals);
    $total = (int)$cntSt->fetchColumn();
    jsonOk(['data'=>$rows,'total'=>$total,'limit'=>$limit,'offset'=>$offset]);
}

// ─── POST ──────────────────────────────────────────────────────────────────────
if ($method === 'POST') {
    $data = body();
    if (empty($data)) jsonError('Sin datos');
    if ($entity === 'patients') {
        try {
            ensurePatientSchema($db);
        } catch (Throwable $e) {
            jsonError('No se pudo preparar la tabla de pacientes. Revise permisos de MySQL para ALTER TABLE. Detalle tecnico: ' . $e->getMessage(), 500);
        }
        normalizePatientData($data);
        if (!trim($data['first_name'] ?? '')) jsonError('Nombre requerido');
        if (!trim($data['last_name']  ?? '')) jsonError('Apellido requerido');
        if (!trim($data['dni'] ?? ''))        jsonError('DNI requerido');
        $chk = $db->prepare("SELECT id FROM patients WHERE dni=? LIMIT 1");
        $chk->execute([trim($data['dni'])]);
        if ($chk->fetch()) jsonError('Ya existe un paciente con ese DNI');
    }
    if ($entity === 'users') {
        if ($user['role'] !== 'Super Admin' && $user['role'] !== 'Administrador') jsonError('Sin permisos para gestionar usuarios', 403);
        if (!trim($data['username'] ?? '')) jsonError('Usuario requerido');
        if (isset($data['podoFunctions'])) { $data['podo_functions'] = $data['podoFunctions']; unset($data['podoFunctions']); }
        $pwd = $data['password'] ?? '';
        if ($pwd) { $data['password_hash'] = password_hash($pwd, PASSWORD_BCRYPT); }
        unset($data['password']);
    }
    if ($entity === 'appointments') { if (!($data['date'] ?? '')) jsonError('Fecha requerida'); }
    if ($entity === 'appointments') { normalizeAppointmentData($data); }
    if ($entity === 'attendances') {
        normalizeAttendanceData($data);
        if (!($data['date'] ?? '')) jsonError('Fecha requerida');
        if (!empty($data['appointment_id'])) {
            $db->prepare("UPDATE appointments SET status='atendida' WHERE id=?")->execute([(int)$data['appointment_id']]);
        }
        if (empty($data['attended_by'])) $data['attended_by'] = $user['name'];
    }
    if ($entity === 'photos') {
        if (!($data['src'] ?? '')) jsonError('Imagen requerida');
        if (!($data['date'] ?? '')) $data['date'] = date('Y-m-d');
    }
    unset($data['id']);
    encodeJsonFields($data);
    $stCols = $db->prepare("DESCRIBE `$table`"); $stCols->execute();
    $validCols = array_column($stCols->fetchAll(), 'Field');
    $filteredData = array_intersect_key($data, array_flip($validCols));
    if (empty($filteredData)) jsonError('Sin campos válidos para insertar');
    $colStr = implode(',', array_map(fn($c)=>"`$c`", array_keys($filteredData)));
    $phStr  = implode(',', array_fill(0, count($filteredData), '?'));
    try {
        $st = $db->prepare("INSERT INTO `$table` ($colStr) VALUES ($phStr)");
        $st->execute(array_values($filteredData));
        $newId = (int)$db->lastInsertId();
    } catch (Throwable $e) {
        $msg = $entity === 'patients'
            ? 'No se pudo grabar el paciente en la base de datos. Verifique los datos obligatorios y vuelva a intentar. Detalle tecnico: ' . $e->getMessage()
            : 'No se pudo insertar el registro: ' . $e->getMessage();
        jsonError($msg, 500);
    }
    $created = false;
    if ($newId > 0) {
        $st2 = $db->prepare("SELECT * FROM `$table` WHERE id=? LIMIT 1");
        $st2->execute([$newId]);
        $created = $st2->fetch();
    }
    if (!$created && $entity === 'patients' && !empty($data['dni'])) {
        $st2 = $db->prepare("SELECT * FROM patients WHERE dni=? ORDER BY id DESC LIMIT 1");
        $st2->execute([trim($data['dni'])]);
        $created = $st2->fetch();
    }
    if (!$created && $entity === 'users' && !empty($data['username'])) {
        $st2 = $db->prepare("SELECT * FROM users WHERE username=? ORDER BY id DESC LIMIT 1");
        $st2->execute([trim($data['username'])]);
        $created = $st2->fetch();
    }
    if (!$created && $entity === 'sales_orders' && !empty($data['code'])) {
        $st2 = $db->prepare("SELECT * FROM sales_orders WHERE code=? ORDER BY id DESC LIMIT 1");
        $st2->execute([trim($data['code'])]);
        $created = $st2->fetch();
    }
    if (!$created) {
        $fallbackId = (int)$db->query("SELECT MAX(id) FROM `$table`")->fetchColumn();
        if ($fallbackId > 0) {
            $st2 = $db->prepare("SELECT * FROM `$table` WHERE id=? LIMIT 1");
            $st2->execute([$fallbackId]);
            $created = $st2->fetch();
        }
    }
    if (!$created) jsonError('Se insertó el registro, pero no se pudo recuperar el ID guardado', 500);
    decodeJsonFields($created);
    if ($entity === 'users') { unset($created['password_hash']); }
    jsonOk($created);
}

// ─── PUT ───────────────────────────────────────────────────────────────────────
if ($method === 'PUT') {
    if (!$id) jsonError('ID requerido para actualizar');
    $data = body();
    if (empty($data)) jsonError('Sin datos');
    unset($data['id']);
    if ($entity === 'patients') {
        try {
            ensurePatientSchema($db);
        } catch (Throwable $e) {
            jsonError('No se pudo preparar la tabla de pacientes. Revise permisos de MySQL para ALTER TABLE. Detalle tecnico: ' . $e->getMessage(), 500);
        }
        normalizePatientData($data);
    }
    if ($entity === 'appointments') { normalizeAppointmentData($data); }
    if ($entity === 'attendances') { normalizeAttendanceData($data); }
    if ($entity === 'users') {
        if ($user['role'] !== 'Super Admin' && $user['role'] !== 'Administrador') jsonError('Sin permisos', 403);
        if (isset($data['podoFunctions'])) { $data['podo_functions'] = $data['podoFunctions']; unset($data['podoFunctions']); }
        if (!empty($data['password'])) { $data['password_hash'] = password_hash($data['password'], PASSWORD_BCRYPT); }
        unset($data['password']);
    }
    encodeJsonFields($data);
    $stCols = $db->prepare("DESCRIBE `$table`"); $stCols->execute();
    $validCols = array_column($stCols->fetchAll(), 'Field');
    $filteredData = array_intersect_key($data, array_flip($validCols));
    if (empty($filteredData)) jsonError('Sin campos válidos para actualizar');
    $setStr = implode(',', array_map(fn($c)=>"`$c`=?", array_keys($filteredData)));
    $vals   = array_values($filteredData); $vals[] = $id;
    $db->prepare("UPDATE `$table` SET $setStr WHERE id=?")->execute($vals);
    $st = $db->prepare("SELECT * FROM `$table` WHERE id=? LIMIT 1"); $st->execute([$id]);
    $updated = $st->fetch();
    if (!$updated) jsonError('Registro no encontrado', 404);
    decodeJsonFields($updated);
    if ($entity === 'users') { unset($updated['password_hash']); }
    jsonOk($updated);
}

// ─── DELETE ────────────────────────────────────────────────────────────────────
if ($method === 'DELETE') {
    if (!$id) jsonError('ID requerido para eliminar');
    if ($entity === 'patients') {
        $db->prepare("DELETE FROM attendances WHERE patient_id=?")->execute([$id]);
        $db->prepare("DELETE FROM appointments WHERE patient_id=?")->execute([$id]);
        $db->prepare("DELETE FROM photos WHERE patient_id=?")->execute([$id]);
        $db->prepare("DELETE FROM laboratory WHERE patient_id=?")->execute([$id]);
    }
    if ($entity === 'users') {
        if ($user['role'] !== 'Super Admin') jsonError('Solo Super Admin puede eliminar usuarios', 403);
        $chk = $db->prepare("SELECT role FROM users WHERE id=? LIMIT 1"); $chk->execute([$id]);
        $target = $chk->fetch();
        if ($target && $target['role'] === 'Super Admin') jsonError('No se puede eliminar al Super Admin');
        $db->prepare("DELETE FROM sessions WHERE user_id=?")->execute([$id]);
    }
    $st = $db->prepare("DELETE FROM `$table` WHERE id=?");
    $st->execute([$id]);
    jsonOk(['deleted'=>true,'id'=>$id]);
}

jsonError('Método no soportado', 405);
