<?php
ob_start();
require_once '../../core/config.php';
require_once '../../core/session.php';
require_once '../../core/database.php';
require_once '../../core/permissions.php';
require_once '../../core/format.php';

require_login();
require_permission('module.crm');

header('Content-Type: application/json; charset=utf-8');


function q_save_json_response(array $payload, $statusCode = 200)
{
    while (ob_get_level() > 0) {
        ob_end_clean();
    }
    http_response_code((int)$statusCode);
    header('Content-Type: application/json; charset=utf-8');
    $flags = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES;
    if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
        $flags |= JSON_INVALID_UTF8_SUBSTITUTE;
    }
    $json = json_encode($payload, $flags);
    if ($json === false || $json === '') {
        $json = '{"success":false,"message":"No fue posible codificar la respuesta del servidor."}';
    }
    echo $json;
    exit;
}

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    q_save_json_response(array('success' => false, 'message' => 'Método no permitido.'), 405);
}

$allowedStatuses = array('Borrador', 'Enviada', 'Aceptada', 'Rechazada', 'Vencida', 'Anulada');

function q_save_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];
}

function q_save_phone($phone)
{
    $phone = preg_replace('/[^0-9]/', '', (string)$phone);
    if ($phone === '') return '';
    if (strlen($phone) === 9) return '56' . $phone;
    if (strlen($phone) === 8) return '569' . $phone;
    return $phone;
}

function q_save_date($date)
{
    return $date ? date('d-m-Y', strtotime($date)) : '-';
}

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

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

function q_save_display_customer($row)
{
    if (!empty($row['legal_name'])) return $row['legal_name'];
    if (!empty($row['commercial_name'])) return $row['commercial_name'];
    return $row['customer_name'] ?? '';
}

function q_save_split_item($item)
{
    $name = (string)($item['name'] ?? '');
    $sku = '';

    if (preg_match('/\s*\(SKU:\s*([^)]+)\)\s*$/i', $name, $m)) {
        $sku = trim($m[1]);
        $name = preg_replace('/\s*\(SKU:\s*([^)]+)\)\s*$/i', '', $name);
    }

    if ($sku === '' && !empty($item['description'])) {
        if (preg_match('/SKU:\s*(.+)$/i', (string)$item['description'], $m)) {
            $sku = trim($m[1]);
        } else {
            $sku = trim((string)$item['description']);
        }
    }

    $item['display_name'] = trim($name);
    $item['sku'] = $sku;
    $item['unit'] = q_save_clean_unit($item['unit'] ?? 'unidad');
    return $item;
}

function q_save_load_quote(PDO $pdo, $id)
{
    $hasInvoiceIssueDate = q_save_col_exists($pdo, 'crm_quotes', 'invoice_issue_date');
    $hasInvoiceNumber = q_save_col_exists($pdo, 'crm_quotes', 'invoice_number');
    $hasPaymentDate = q_save_col_exists($pdo, 'crm_quotes', 'payment_date');
    $hasDeletedAt = q_save_col_exists($pdo, 'crm_quotes', 'deleted_at');
    $hasDeletedBy = q_save_col_exists($pdo, 'crm_quotes', 'deleted_by');
    $hasDeletedReason = q_save_col_exists($pdo, 'crm_quotes', 'deleted_reason');

    $stmt = $pdo->prepare("
        SELECT
            q.*,
            " . ($hasInvoiceIssueDate ? "q.invoice_issue_date" : "NULL AS invoice_issue_date") . ",
            " . ($hasInvoiceNumber ? "q.invoice_number" : "NULL AS invoice_number") . ",
            " . ($hasPaymentDate ? "q.payment_date" : "NULL AS payment_date") . ",
            c.customer_name,
            c.commercial_name,
            c.legal_name,
            c.customer_tax_id,
            c.email,
            c.phone,
            c.address,
            c.district,
            c.city,
            o.title AS opportunity_title,
            " . ($hasDeletedAt ? "q.deleted_at" : "NULL AS deleted_at") . ",
            " . ($hasDeletedBy ? "q.deleted_by" : "NULL AS deleted_by") . ",
            " . ($hasDeletedReason ? "q.deleted_reason" : "NULL AS deleted_reason") . "
        FROM crm_quotes q
        JOIN crm_customers c ON q.customer_id = c.id
        LEFT JOIN crm_opportunities o ON q.opportunity_id = o.id
        WHERE q.id = ?
          " . ($hasDeletedAt ? "AND q.deleted_at IS NULL" : "") . "
        LIMIT 1
    ");
    $stmt->execute(array($id));
    $quote = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$quote) return null;

    $stmtItems = $pdo->prepare("SELECT * FROM crm_quote_items WHERE quote_id = ? ORDER BY id ASC");
    $stmtItems->execute(array($id));

    $items = array();
    foreach ($stmtItems->fetchAll(PDO::FETCH_ASSOC) as $item) {
        $items[] = q_save_split_item($item);
    }

    return array(
        'id' => (int)$quote['id'],
        'opportunity_id' => (int)($quote['opportunity_id'] ?? 0),
        'customer_id' => (int)$quote['customer_id'],
        'quote_number' => $quote['quote_number'],
        'subtotal' => (float)$quote['subtotal'],
        'discount_percent' => (float)$quote['discount_percent'],
        'discount_amount' => (float)$quote['discount_amount'],
        'iva' => (float)$quote['iva'],
        'total' => (float)$quote['total'],
        'notes' => $quote['notes'] ?? '',
        'status' => $quote['status'],
        'invoice_issue_date' => q_save_date($quote['invoice_issue_date'] ?? null),
        'invoice_issue_date_raw' => $quote['invoice_issue_date'] ?? '',
        'invoice_number' => $quote['invoice_number'] ?? '',
        'payment_date' => q_save_date($quote['payment_date'] ?? null),
        'payment_date_raw' => $quote['payment_date'] ?? '',
        'created_at' => !empty($quote['created_at']) ? date('d-m-Y', strtotime($quote['created_at'])) : '-',
        'created_at_raw' => $quote['created_at'],
        'customer_name' => q_save_display_customer($quote),
        'customer_tax_id' => $quote['customer_tax_id'] ?? '',
        'email' => $quote['email'] ?? '',
        'phone' => $quote['phone'] ?? '',
        'whatsapp_phone' => q_save_phone($quote['phone'] ?? ''),
        'address' => $quote['address'] ?? '',
        'district' => $quote['district'] ?? '',
        'city' => $quote['city'] ?? '',
        'opportunity_title' => !empty($quote['opportunity_title']) ? $quote['opportunity_title'] : 'Venta directa',
        'deleted_at' => $quote['deleted_at'] ?? '',
        'deleted_by' => $quote['deleted_by'] ?? '',
        'deleted_reason' => $quote['deleted_reason'] ?? '',
        'is_deleted' => !empty($quote['deleted_at']),
        'items' => $items
    );
}

$quoteId = isset($_POST['quote_id']) ? (int)$_POST['quote_id'] : 0;
$postedCustomerId = isset($_POST['customer_id']) ? (int)$_POST['customer_id'] : 0;
$postedOpportunityId = isset($_POST['opportunity_id']) && (int)$_POST['opportunity_id'] > 0 ? (int)$_POST['opportunity_id'] : null;
$status = trim($_POST['status'] ?? 'Borrador');
$discountPercent = max(0, min(100, (float)($_POST['discount_percent'] ?? 0)));
$notes = trim($_POST['notes'] ?? '');
$items = $_POST['items'] ?? array();

if (!in_array($status, $allowedStatuses, true)) {
    q_save_json_response(array('success' => false, 'message' => 'Estado inválido.'), 422);
}

if (!is_array($items) || count($items) === 0) {
    q_save_json_response(array('success' => false, 'message' => 'Debes agregar al menos un producto.'), 422);
}

$cleanItems = array();
$subtotal = 0;

foreach ($items as $item) {
    $name = trim((string)($item['name'] ?? ''));
    $sku = trim((string)($item['sku'] ?? ''));
    $pid = trim((string)($item['product_id'] ?? 'manual'));
    $qty = max(0.001, parse_quantity($item['quantity'] ?? 1));
    $price = max(0, (float)($item['price_unit'] ?? 0));
    $unit = q_save_clean_unit($item['unit'] ?? 'unidad');

    if ($name === '' || $price <= 0) {
        continue;
    }

    $line = round($qty * $price, 2);
    $subtotal += $line;

    $storedName = $name;
    if ($sku !== '' && stripos($storedName, '(SKU:') === false) {
        $storedName .= ' (SKU: ' . $sku . ')';
    }

    $cleanItems[] = array(
        'product_id' => $pid !== '' ? $pid : 'manual',
        'name' => $storedName,
        'description' => $sku !== '' ? 'SKU: ' . $sku : '',
        'quantity' => $qty,
        'unit' => $unit,
        'price_unit' => $price,
        'total_item' => $line
    );
}

if (empty($cleanItems) || $subtotal <= 0) {
    q_save_json_response(array('success' => false, 'message' => 'Los productos deben tener nombre y precio mayor a cero.'), 422);
}

$discountAmount = round($subtotal * ($discountPercent / 100), 2);
$net = round($subtotal - $discountAmount, 2);
$iva = round($net * 0.19, 2);
$total = round($net + $iva, 2);

try {
    require_once '../../services/crm/QuoteNumberService.php';
    require_once '../../services/crm/CustomerService.php';
    require_once '../../services/crm/QuoteLifecycleService.php';
    require_once '../../services/crm/TimelineAuditService.php';

    $pdo = db();
    $customerService = new CustomerService($pdo);
    $quoteLifecycle = new QuoteLifecycleService($pdo);
    $timelineAudit = new TimelineAuditService($pdo);
    $hasDeletedAt = q_save_col_exists($pdo, 'crm_quotes', 'deleted_at');

    if ($quoteId <= 0 && $postedOpportunityId === null) {
        $customerService->requireActiveById($postedCustomerId, 'El cliente seleccionado no existe o está desactivado.');
    }

    $pdo->beginTransaction();

    $isEdit = $quoteId > 0;
    $customerId = $postedCustomerId;
    $opportunityId = $postedOpportunityId;
    $oldQuote = null;
    $oldItems = array();

    if ($isEdit) {
        $stmtOld = $pdo->prepare("SELECT * FROM crm_quotes WHERE id = ? " . ($hasDeletedAt ? "AND deleted_at IS NULL " : "") . "LIMIT 1 FOR UPDATE");
        $stmtOld->execute(array($quoteId));
        $oldQuote = $stmtOld->fetch(PDO::FETCH_ASSOC);

        if (!$oldQuote) {
            throw new Exception('Cotización no encontrada.');
        }

        $customerId = (int)$oldQuote['customer_id'];
        $customerService->requireActiveById($customerId, 'El cliente asociado a esta cotización está desactivado. Reactívalo antes de editar.');
        $opportunityId = !empty($oldQuote['opportunity_id']) ? (int)$oldQuote['opportunity_id'] : null;
        $quoteNumber = $oldQuote['quote_number'];

        $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 subtotal = ?, discount_percent = ?, discount_amount = ?, iva = ?, total = ?, notes = ?, status = ?
            WHERE id = ?
        ");
        $stmt->execute(array($subtotal, $discountPercent, $discountAmount, $iva, $total, $notes, $status, $quoteId));

        $pdo->prepare("DELETE FROM crm_quote_items WHERE quote_id = ?")->execute(array($quoteId));
        $activityTitle = 'Cotización Modificada (' . $quoteNumber . ')';
        $activityVerb = 'actualizado';
    } else {
        if ($opportunityId !== null) {
            $stmtOpp = $pdo->prepare("
                SELECT o.customer_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
            ");
            $stmtOpp->execute(array($opportunityId));
            $oppCustomerId = (int)$stmtOpp->fetchColumn();

            if ($oppCustomerId <= 0) {
                throw new Exception('Oportunidad no encontrada.');
            }

            $customerId = $oppCustomerId;
        }

        if ($customerId <= 0) {
            throw new Exception('Debes seleccionar un cliente.');
        }

        $quoteNumber = crm_next_quote_number($pdo);

        $stmt = $pdo->prepare("
            INSERT INTO crm_quotes
            (opportunity_id, customer_id, quote_number, subtotal, discount_percent, discount_amount, iva, total, notes, status, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
        ");
        $stmt->execute(array($opportunityId, $customerId, $quoteNumber, $subtotal, $discountPercent, $discountAmount, $iva, $total, $notes, $status));
        $quoteId = (int)$pdo->lastInsertId();

        $activityTitle = 'Cotización Creada (' . $quoteNumber . ')';
        $activityVerb = 'creado';
    }

    $customer = $customerService->requireActiveById($customerId, 'Cliente no encontrado o desactivado.');

    $stmtItem = $pdo->prepare("
        INSERT INTO crm_quote_items
        (quote_id, product_id, name, description, quantity, unit, price_unit, total_item)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    ");

    foreach ($cleanItems as $item) {
        $stmtItem->execute(array($quoteId, $item['product_id'], $item['name'], $item['description'], $item['quantity'], $item['unit'], $item['price_unit'], $item['total_item']));
    }

    $pdfPath = '/modules/crm/quote_pdf.php?id=' . $quoteId;
    $legacyPdfPath = '/modules/crm/quote_view.php?id=' . $quoteId;
    $documentName = 'Cotización ' . $quoteNumber;

    if ($isEdit) {
        $stmtDoc = $pdo->prepare("
            UPDATE crm_documents
            SET opportunity_id = ?, customer_id = ?, document_date = CURDATE(), net_amount = ?, total_amount = ?, file_path = ?
            WHERE file_path IN (?, ?)
        ");
        $stmtDoc->execute(array($opportunityId, $customerId, $net, $total, $pdfPath, $pdfPath, $legacyPdfPath));

        if ($stmtDoc->rowCount() === 0) {
            $stmtDocInsert = $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 (?, ?, CURDATE(), 'cotizacion', ?, ?, ?, ?, ?)
            ");
            $stmtDocInsert->execute(array($opportunityId, $customerId, $quoteNumber, $documentName, $net, $total, $pdfPath));
        }
    } else {
        $stmtDocInsert = $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 (?, ?, CURDATE(), 'cotizacion', ?, ?, ?, ?, ?)
        ");
        $stmtDocInsert->execute(array($opportunityId, $customerId, $quoteNumber, $documentName, $net, $total, $pdfPath));
    }

    $sessionUser = current_user();
    $ownerName = !empty($sessionUser['name']) ? $sessionUser['name'] : (!empty($sessionUser['email']) ? $sessionUser['email'] : 'Sistema');
    $customerName = q_save_display_customer($customer);

    $newQuoteForAudit = array(
        'quote_number' => $quoteNumber,
        'status' => $status,
        'subtotal' => $subtotal,
        'discount_percent' => $discountPercent,
        'discount_amount' => $discountAmount,
        'iva' => $iva,
        'total' => $total,
        'notes' => $notes,
        'invoice_issue_date' => $isEdit && $oldQuote ? ($oldQuote['invoice_issue_date'] ?? null) : null,
        'payment_date' => $isEdit && $oldQuote ? ($oldQuote['payment_date'] ?? null) : null,
    );

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

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

    $quote = q_save_load_quote($pdo, $quoteId);

    $pdo->commit();

    q_save_json_response(array(
        'success' => true,
        'mode' => $isEdit ? 'edit' : 'create',
        'message' => $isEdit ? 'Cotización actualizada correctamente.' : 'Cotización creada correctamente.',
        'quote_id' => $quoteId,
        'quote_number' => $quoteNumber,
        'quote' => $quote
    ));
} catch (Throwable $e) {
    if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
        $pdo->rollBack();
    }

    q_save_json_response(array('success' => false, 'message' => $e->getMessage()), 500);
}
