<?php
/**
 * ICYLAB OS - Procesador de Persistencia para Cotizaciones Híbridas (Corregido)
 */

require_once '../../core/config.php';
require_once '../../core/session.php';
require_once '../../core/database.php';
require_once '../../core/format.php';
require_once '../../services/crm/QuoteNumberService.php';
require_once '../../services/crm/CustomerService.php';
require_once '../../services/crm/QuoteLifecycleService.php';
require_once '../../services/crm/TimelineAuditService.php';
require_once '../../core/notifications.php';

require_login();
$pdo = db();

function quote_store_allowed_units()
{
    return array('unidad', 'mL', 'L', 'g', 'kg', 'pack', 'caja', 'servicio');
}

function quote_store_clean_unit($unit)
{
    $unit = trim((string)$unit);
    return in_array($unit, quote_store_allowed_units(), true) ? $unit : 'unidad';
}

function quote_store_col_exists(PDO $pdo, $table, $column)
{
    static $cache = array();
    $key = $table . '.' . $column;
    if (array_key_exists($key, $cache)) return $cache[$key];
    $stmt = $pdo->prepare("SHOW COLUMNS FROM `" . str_replace('`', '', $table) . "` LIKE ?");
    $stmt->execute(array($column));
    $cache[$key] = (bool)$stmt->fetch(PDO::FETCH_ASSOC);
    return $cache[$key];
}

$hasQuoteDeletedAt = quote_store_col_exists($pdo, 'crm_quotes', 'deleted_at');
$customerService = new CustomerService($pdo);
$quoteLifecycle = new QuoteLifecycleService($pdo);
$timelineAudit = new TimelineAuditService($pdo);

// Transformamos el 0 en NULL para evitar errores de Foreign Key en la base de datos
$rawOppId        = isset($_POST['opportunity_id']) ? (int)$_POST['opportunity_id'] : 0;
$opportunityId   = $rawOppId > 0 ? $rawOppId : null; 
$customerId      = isset($_POST['customer_id']) ? (int)$_POST['customer_id'] : 0;
$quoteId         = isset($_POST['quote_id']) ? (int)$_POST['quote_id'] : 0;
$notes           = isset($_POST['notes']) ? trim($_POST['notes']) : '';
$status          = isset($_POST['status']) ? trim($_POST['status']) : 'Borrador'; 
$discountPercent = isset($_POST['discount_percent']) ? (float)$_POST['discount_percent'] : 0.0;
$items           = isset($_POST['items']) ? $_POST['items'] : array();
$isAjax          = isset($_POST['ajax']) && $_POST['ajax'] == '1';

if ($customerId === 0 || empty($items)) {
    if ($isAjax) { echo json_encode(['success' => false, 'error' => 'Parámetros inválidos. Se requiere un cliente asignado y al menos un producto.']); exit; }
    die('Error: Parámetros inválidos. Se requiere un cliente asignado.');
}

try {
    $customerService->requireActiveById($customerId, 'El cliente seleccionado no existe o está desactivado.');
} catch (Throwable $e) {
    if ($isAjax) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); exit; }
    die('Error: ' . $e->getMessage());
}

if ($opportunityId !== null) {
    $stmtActiveOpp = $pdo->prepare("
        SELECT o.id
        FROM crm_opportunities o
        JOIN crm_customers c ON c.id = o.customer_id
        WHERE o.id = ?
          AND o.deleted_at IS NULL
          AND c.deleted_at IS NULL
        LIMIT 1
    ");
    $stmtActiveOpp->execute(array($opportunityId));
    if (!$stmtActiveOpp->fetchColumn()) {
        if ($isAjax) { echo json_encode(['success' => false, 'error' => 'La oportunidad seleccionada no existe, fue eliminada o pertenece a un cliente desactivado.']); exit; }
        die('Error: oportunidad inválida o cliente desactivado.');
    }
}

$sessionUser = current_user();
$currentUser = !empty($sessionUser['name']) ? $sessionUser['name'] : (!empty($sessionUser['email']) ? $sessionUser['email'] : 'Sistema');

// ==========================================================================
// INTERCEPCION RAPIDA: Cambio de Estado desde Ficha (AJAX)
// ========================================================================== 
if (isset($_POST['items']) && $_POST['items'] === 'dummy' && $quoteId > 0) {
    try {
        $result = $quoteLifecycle->changeStatus($quoteId, $status, array(
            'owner_name' => $currentUser,
            'record_activity' => true
        ));

        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(array('success' => true, 'lifecycle' => $result), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }

        header("Location: /modules/crm/quotes.php");
        exit;
    } catch (Throwable $e) {
        if ($isAjax) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(array('success' => false, 'error' => $e->getMessage()), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            exit;
        }
        die("Error critico AJAX: " . $e->getMessage());
    }
}

// ==========================================================================
// FLUJO NORMAL: CREACIÓN / EDICIÓN
// ==========================================================================
$isEdit = ($quoteId > 0);
$oldQuote = null;
$oldItems = array();

try {
    $pdo->beginTransaction();

    $subtotal = 0;
    $validItems = array();
    foreach ($items as $item) {
        $qty = parse_quantity($item['quantity'] ?? 0);
        $price = (float)($item['price_unit'] ?? 0);
        if ($qty > 0 && $price >= 0) {
            $itemTotal = $qty * $price;
            $subtotal += $itemTotal;
            $validItems[] = array(
                'product_id'  => $item['product_id'] ?? 'S/I',
                'name'        => trim($item['name'] ?? 'Producto'),
                'sku'         => trim($item['sku'] ?? 'S/S'),
                'quantity'    => $qty,
                'unit'        => quote_store_clean_unit($item['unit'] ?? 'unidad'),
                'price_unit'  => $price,
                'total_item'  => $itemTotal
            );
        }
    }

    $discountAmount  = $subtotal * ($discountPercent / 100);
    $netWithDiscount = $subtotal - $discountAmount;
    $iva             = $netWithDiscount * 0.19;
    $totalGeneral    = $netWithDiscount + $iva;

    if ($isEdit) {
        $stmtNum = $pdo->prepare("SELECT * FROM crm_quotes WHERE id = ? " . ($hasQuoteDeletedAt ? "AND deleted_at IS NULL " : "") . "LIMIT 1 FOR UPDATE");
        $stmtNum->execute(array($quoteId));
        $oldQuote = $stmtNum->fetch(PDO::FETCH_ASSOC);
        if (!$oldQuote) {
            throw new Exception('Cotización no encontrada o eliminada. Restáurala antes de editar.');
        }
        $quoteNumber = $oldQuote['quote_number'] ?? 'S/N';

        $stmtOldItems = $pdo->prepare("SELECT * FROM crm_quote_items WHERE quote_id = ? ORDER BY id ASC");
        $stmtOldItems->execute(array($quoteId));
        $oldItems = $stmtOldItems->fetchAll(PDO::FETCH_ASSOC);

        $stmt = $pdo->prepare("UPDATE crm_quotes SET discount_percent = ?, discount_amount = ?, subtotal = ?, iva = ?, total = ?, notes = ?, status = ? WHERE id = ?" . ($hasQuoteDeletedAt ? " AND deleted_at IS NULL" : ""));
        $stmt->execute(array($discountPercent, $discountAmount, $subtotal, $iva, $totalGeneral, $notes, $status, $quoteId));

        $pdfPath = "/modules/crm/quote_view.php?id=" . $quoteId;
        $stmtDocUpdate = $pdo->prepare("UPDATE crm_documents SET net_amount = ?, total_amount = ? WHERE file_path = ?");
        $stmtDocUpdate->execute(array($netWithDiscount, $totalGeneral, $pdfPath));

        $pdo->prepare("DELETE FROM crm_quote_items WHERE quote_id = ?")->execute(array($quoteId));
    } else {
        $quoteNumber = crm_next_quote_number($pdo);

        $stmt = $pdo->prepare("INSERT INTO crm_quotes (opportunity_id, customer_id, quote_number, discount_percent, discount_amount, subtotal, iva, total, notes, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
        $stmt->execute(array($opportunityId, $customerId, $quoteNumber, $discountPercent, $discountAmount, $subtotal, $iva, $totalGeneral, $notes, $status));
        $quoteId = $pdo->lastInsertId();
        
        $pdfPath = "/modules/crm/quote_view.php?id=" . $quoteId;
        $documentName = 'Cotización ' . $quoteNumber; 
        
        // ¡FIX!: Insertamos también el customer_id
        $stmtDoc = $pdo->prepare("INSERT INTO crm_documents (opportunity_id, customer_id, document_date, document_type, document_number, document_name, net_amount, total_amount, file_path) VALUES (?, ?, NOW(), 'cotizacion', ?, ?, ?, ?, ?)");
        $stmtDoc->execute(array($opportunityId, $customerId, $quoteNumber, $documentName, $netWithDiscount, $totalGeneral, $pdfPath));
    }

    // ¡FIX!: Eliminada la columna `description` para que no colapse MySQL
    $stmtItem = $pdo->prepare("INSERT INTO crm_quote_items (quote_id, product_id, name, quantity, unit, price_unit, total_item) VALUES (?, ?, ?, ?, ?, ?, ?)");
    $auditItems = array();
    foreach ($validItems as $vItem) {
        $fullName = $vItem['name'] . (($vItem['sku'] !== 'S/S' && !empty($vItem['sku'])) ? ' (SKU: ' . $vItem['sku'] . ')' : '');
        $stmtItem->execute(array($quoteId, $vItem['product_id'], $fullName, $vItem['quantity'], $vItem['unit'], $vItem['price_unit'], $vItem['total_item']));
        $auditItems[] = array(
            'product_id' => $vItem['product_id'],
            'name' => $fullName,
            'quantity' => $vItem['quantity'],
            'unit' => $vItem['unit'],
            'price_unit' => $vItem['price_unit'],
            'total_item' => $vItem['total_item']
        );
    }
    
    $customer = $customerService->requireActiveById($customerId, 'Cliente no encontrado o desactivado.');
    $customerName = !empty($customer['legal_name']) ? $customer['legal_name'] : (!empty($customer['commercial_name']) ? $customer['commercial_name'] : ($customer['customer_name'] ?? 'Cliente General'));

    $newQuoteForAudit = array(
        'quote_number' => $quoteNumber,
        'status' => $status,
        'subtotal' => $subtotal,
        'discount_percent' => $discountPercent,
        'discount_amount' => $discountAmount,
        'iva' => $iva,
        'total' => $totalGeneral,
        'notes' => $notes,
        'invoice_issue_date' => null,
        'payment_date' => null,
    );

    if ($isEdit && $oldQuote) {
        $changes = $timelineAudit->buildQuoteUpdateChanges($oldQuote, $newQuoteForAudit, $oldItems, $auditItems);
        if (!empty($changes)) {
            $timelineAudit->insertActivity(
                $opportunityId,
                $customerId,
                $customerName,
                'cotizacion',
                'Cotización Modificada (' . $quoteNumber . ')',
                $changes,
                $currentUser,
                'Presupuesto ' . $quoteNumber . ' actualizado.'
            );
        }
    } else {
        $changes = $timelineAudit->buildQuoteCreatedChanges($newQuoteForAudit, $auditItems);
        $timelineAudit->insertActivity(
            $opportunityId,
            $customerId,
            $customerName,
            'cotizacion',
            'Cotización Creada (' . $quoteNumber . ')',
            $changes,
            $currentUser,
            'Presupuesto ' . $quoteNumber . ' creado.'
        );
    }

    $quoteLifecycle->syncAfterQuoteSave($quoteId, $isEdit && isset($oldQuote) ? (string)$oldQuote['status'] : null, $status, array(
        'owner_name' => $currentUser,
        'record_status_activity' => false
    ));

    if (!$isEdit) {
        $recipientUserId = notification_actor_user_id();
        if ($opportunityId !== null) {
            $stmtOwner = $pdo->prepare("SELECT owner_user_id FROM crm_opportunities WHERE id = ? LIMIT 1");
            $stmtOwner->execute(array($opportunityId));
            $ownerFromOpportunity = $stmtOwner->fetchColumn();
            if ($ownerFromOpportunity) {
                $recipientUserId = (int)$ownerFromOpportunity;
            }
        }

        $quoteUrl = '/modules/crm/quote_view.php?id=' . (int)$quoteId;
        $opportunityUrl = $opportunityId !== null ? '/modules/crm/opportunity.php?id=' . (int)$opportunityId : $quoteUrl;
        $notificationBody = 'Cliente: ' . $customerName . "\nCotización: " . $quoteNumber . "\nEstado: " . $status . "\nTotal facturable: $" . number_format($totalGeneral, 0, ',', '.');

        notify_crm_owner_or_admin_event(
            'quote.created',
            $recipientUserId,
            'Cotización creada: ' . $quoteNumber,
            $notificationBody,
            $quoteUrl,
            'quote',
            (int)$quoteId,
            array('dedupe_key' => 'quote.created:' . (int)$quoteId, 'metadata' => array('total' => (float)$totalGeneral, 'discount_percent' => (float)$discountPercent))
        );

        if ($status === 'Aceptada') {
            notify_crm_owner_or_admin_event(
                'quote.accepted',
                $recipientUserId,
                'Cotización aceptada: ' . $quoteNumber,
                $notificationBody,
                $quoteUrl,
                'quote',
                (int)$quoteId,
                array('dedupe_key' => 'quote.accepted:' . (int)$quoteId, 'metadata' => array('total' => (float)$totalGeneral, 'discount_percent' => (float)$discountPercent))
            );
        } elseif ($status === 'Rechazada') {
            notify_crm_owner_or_admin_event(
                'quote.rejected',
                $recipientUserId,
                'Cotización rechazada: ' . $quoteNumber,
                $notificationBody,
                $quoteUrl,
                'quote',
                (int)$quoteId,
                array('dedupe_key' => 'quote.rejected:' . (int)$quoteId, 'metadata' => array('total' => (float)$totalGeneral, 'discount_percent' => (float)$discountPercent))
            );
        }

        if ($opportunityId !== null && in_array($status, array('Facturada', 'Esperando pago'), true)) {
            notify_crm_owner_or_admin_event(
                'opportunity.facturada',
                $recipientUserId,
                'Oportunidad facturada por cotización: ' . $quoteNumber,
                $notificationBody,
                $opportunityUrl,
                'opportunity',
                (int)$opportunityId,
                array('dedupe_key' => 'opportunity.facturada:' . (int)$opportunityId, 'metadata' => array('total' => (float)$totalGeneral, 'quote_id' => (int)$quoteId))
            );
            notify_crm_owner_or_admin_event(
                'billing.por_cobrar',
                $recipientUserId,
                'Cobranza por cobrar: ' . $quoteNumber,
                $notificationBody,
                $opportunityUrl,
                'opportunity',
                (int)$opportunityId,
                array('dedupe_key' => 'billing.por_cobrar:' . (int)$opportunityId . ':' . (int)$quoteId, 'metadata' => array('total' => (float)$totalGeneral, 'quote_id' => (int)$quoteId))
            );
        } elseif ($opportunityId !== null && $status === 'Pagada') {
            notify_crm_owner_or_admin_event(
                'billing.paid',
                $recipientUserId,
                'Cobranza pagada: ' . $quoteNumber,
                $notificationBody,
                $opportunityUrl,
                'opportunity',
                (int)$opportunityId,
                array('dedupe_key' => 'billing.paid:' . (int)$opportunityId . ':' . (int)$quoteId, 'metadata' => array('total' => (float)$totalGeneral, 'quote_id' => (int)$quoteId))
            );
        }
    }

    notify_discount_exception_event(
        (int)$quoteId,
        $quoteNumber,
        $customerName,
        (float)$discountPercent,
        (float)$totalGeneral,
        array('dedupe_key' => 'quote.discount_high:' . (int)$quoteId . ':' . number_format((float)$discountPercent, 2, '.', ''))
    );

    $pdo->commit();
    unset($_SESSION['quote_preview_old']);

    // Si nos llamó el modal por AJAX, le respondemos JSON
    if ($isAjax) {
        header('Content-Type: application/json');
        echo json_encode(['success' => true, 'quote_id' => $quoteId]);
        exit;
    }

    header("Location: /modules/crm/quote_view.php?id=" . $quoteId);
    exit;

} catch (Throwable $e) {
    if ($pdo->inTransaction()) $pdo->rollBack();
    if ($isAjax) {
        header('Content-Type: application/json');
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
        exit;
    }
    die("Error crítico de persistencia: " . $e->getMessage());
}