<?php
require_once '../includes/auth.php';
require_once '../db.php';
require_once '../includes/invoice_number.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    die("Invalid Request");
}

$sales_invoice_id = (int)$_POST['sales_invoice_id'];
$action           = $_POST['action']; // save | final

$client_id = (int)$_POST['client_id'];
if ($client_id <= 0) die("Client is required.");

$invoice_date = $_POST['invoice_date'] ?? date('Y-m-d');

// Load existing invoice
$existingInv = $conn->query("
    SELECT invoice_no, status FROM sales_invoices WHERE id = $sales_invoice_id AND company_id = $cid
")->fetch_assoc();
if (!$existingInv || $existingInv['status'] !== 'draft') {
    die("Only draft invoices can be edited.");
}
$invoice_no = $existingInv['invoice_no']; // keep existing number by default

$product_ids  = $_POST['product_id'];
$qtys         = $_POST['qty'];
$sell_prices  = $_POST['sell_price'];
$disc_types   = $_POST['disc_type'];
$disc_vals    = $_POST['disc_value'];
$descriptions = $_POST['description'] ?? [];
$mrp_inputs   = $_POST['mrp'] ?? [];

$overall_disc_type = $_POST['overall_discount_type'] ?? null;
$overall_disc_val  = floatval($_POST['overall_discount_value'] ?? 0);
$round_off         = floatval($_POST['round_off'] ?? 0);

// Block edit if any sales returns exist for this invoice
$hasReturns = $conn->query("
    SELECT COUNT(*) AS cnt
    FROM sales_return_items sri
    JOIN sales_items si ON si.id = sri.sales_item_id
    WHERE si.sales_invoice_id = $sales_invoice_id
")->fetch_assoc()['cnt'];

if ($hasReturns > 0) {
    header("Location: edit.php?id=$sales_invoice_id&error=has_returns");
    exit;
}

function fifoAllocate(mysqli $conn, int $product_id, float $qty_needed): array {
    $batches = $conn->query("
        SELECT pi.id AS purchase_item_id, pi.batch_no, pi.product_id,
               pi.purchase_price, pi.gst_percent, pi.available_qty
        FROM purchase_items pi
        JOIN purchase_orders po ON po.id = pi.purchase_order_id
        WHERE pi.product_id = $product_id
          AND pi.available_qty > 0
          AND po.status = 'final'
        ORDER BY pi.created_at ASC
    ");
    $allocations = [];
    $remaining   = $qty_needed;
    while ($remaining > 0.001 && ($b = $batches->fetch_assoc())) {
        $take = min($remaining, (float)$b['available_qty']);
        $allocations[] = [
            'purchase_item_id' => (int)$b['purchase_item_id'],
            'batch_no'         => $b['batch_no'],
            'product_id'       => (int)$b['product_id'],
            'purchase_price'   => (float)$b['purchase_price'],
            'gst_percent'      => (float)$b['gst_percent'],
            'qty'              => $take,
        ];
        $remaining -= $take;
    }
    if ($remaining > 0.001) return [];
    return $allocations;
}

function getFirstBatch(mysqli $conn, int $product_id): ?array {
    return $conn->query("
        SELECT pi.id AS purchase_item_id, pi.batch_no,
               pi.purchase_price, pi.gst_percent
        FROM purchase_items pi
        JOIN purchase_orders po ON po.id = pi.purchase_order_id
        WHERE pi.product_id = $product_id
          AND po.status = 'final'
        ORDER BY pi.created_at ASC
        LIMIT 1
    ")->fetch_assoc() ?: null;
}

// ── Pre-validate stock BEFORE any DB writes ──────────────────────────────
$_prePrefix = $conn->query("SELECT invoice_prefix FROM company_master WHERE id = $cid LIMIT 1")->fetch_assoc()['invoice_prefix'] ?? '';
foreach ($product_ids as $_pi => $_pid) {
    $_pid = (int)$_pid;
    if ($_pid <= 0) continue;
    $_qty = floatval($qtys[$_pi] ?? 0);
    if ($_qty <= 0) continue;
    $_avail = (float)$conn->query("
        SELECT COALESCE(SUM(pi.available_qty), 0) AS total
        FROM purchase_items pi
        JOIN purchase_orders po ON po.id = pi.purchase_order_id
        WHERE pi.product_id = $_pid AND pi.available_qty > 0 AND po.status = 'final'
    ")->fetch_assoc()['total'];
    if ($_avail < $_qty) {
        $_pname = $conn->query("SELECT name FROM products WHERE id = $_pid")->fetch_assoc()['name'] ?? "Product #$_pid";
        $_pcode = $_prePrefix . '-' . $_pid;
        echo '<!DOCTYPE html><html><head><meta charset="utf-8"><style>
            body{font-family:Arial,sans-serif;background:#f8fafc;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;}
            .box{background:#fff;border:1px solid #fca5a5;border-radius:10px;padding:32px 40px;max-width:500px;width:90%;text-align:center;}
            .icon{font-size:42px;margin-bottom:10px;}
            h3{color:#dc2626;margin:0 0 12px;font-size:18px;}
            p{color:#374151;margin:0 0 6px;font-size:14px;line-height:1.6;}
            .detail{background:#fef2f2;border-radius:6px;padding:10px 16px;margin:14px 0;font-size:13px;}
            a{display:inline-block;margin-top:16px;padding:9px 22px;background:#2563eb;color:#fff;border-radius:6px;text-decoration:none;font-size:14px;font-weight:600;}
        </style></head><body>
        <div class="box">
            <div class="icon">&#9888;&#65039;</div>
            <h3>Insufficient Stock</h3>
            <p>Cannot save invoice. Product availability is insufficient.</p>
            <div class="detail">
                <strong>' . htmlspecialchars($_pname) . '</strong><br>
                Code: ' . htmlspecialchars($_pcode) . '<br>
                Available: ' . number_format($_avail, 2) . ' &nbsp;|&nbsp; Required: ' . number_format($_qty, 2) . '
            </div>
            <a href="javascript:history.back()">&#8592; Go Back &amp; Fix</a>
        </div>
        </body></html>';
        exit;
    }
}
// ─────────────────────────────────────────────────────────────────────────────

$conn->begin_transaction();

try {
    // Delete existing items for this invoice
    $conn->query("DELETE FROM sales_items WHERE sales_invoice_id = $sales_invoice_id");

    if ($action === 'final') {
        $invoice_no = generateFinalInvoiceNo($client_id, $conn, $cid);
    }

    $gross_total   = 0;
    $total_taxable = 0;
    $total_gst     = 0;

    foreach ($product_ids as $i => $product_id) {
        $product_id  = (int)$product_id;
        if ($product_id <= 0) continue;

        $qty             = floatval($qtys[$i] ?? 0);
        if ($qty <= 0) continue;

        $sell_price_incl = floatval($sell_prices[$i] ?? 0);
        $disc_type       = $disc_types[$i] ?? null;
        $disc_val        = floatval($disc_vals[$i] ?? 0);
        $description     = $descriptions[$i] ?? '';

        $final_unit_incl = $sell_price_incl;
        if ($disc_type === 'percent') $final_unit_incl -= $sell_price_incl * $disc_val / 100;
        elseif ($disc_type === 'amount') $final_unit_incl -= $disc_val;
        if ($final_unit_incl < 0) $final_unit_incl = 0;

        if ($action === 'final') {
            $allocations = fifoAllocate($conn, $product_id, $qty);
            if (empty($allocations)) {
                throw new Exception("Insufficient stock for product ID $product_id. Please check available inventory.");
            }
        } else {
            $bInfo = getFirstBatch($conn, $product_id);
            if (!$bInfo) continue;
            $allocations = [[
                'purchase_item_id' => (int)$bInfo['purchase_item_id'],
                'batch_no'         => $bInfo['batch_no'],
                'product_id'       => $product_id,
                'purchase_price'   => (float)$bInfo['purchase_price'],
                'gst_percent'      => (float)$bInfo['gst_percent'],
                'qty'              => $qty,
            ]];
        }

        foreach ($allocations as $alloc) {
            $pi_id          = $alloc['purchase_item_id'];
            $gstp           = $alloc['gst_percent'];
            $purchase_price = $alloc['purchase_price'];
            $alloc_qty      = $alloc['qty'];
            $batch_no       = $alloc['batch_no'];

            $final_unit_excl = ($gstp > 0) ? ($final_unit_incl / (1 + $gstp / 100)) : $final_unit_incl;
            $taxable         = $final_unit_excl * $alloc_qty;
            $gst_amt         = ($final_unit_incl - $final_unit_excl) * $alloc_qty;
            $line_total      = $final_unit_incl * $alloc_qty;

            $gross_total   += $line_total;
            $total_taxable += $taxable;
            $total_gst     += $gst_amt;

            $stmtItem = $conn->prepare("
                INSERT INTO sales_items
                (sales_invoice_id, purchase_item_id, product_id, description, batch_no,
                 sold_qty, selling_price_incl, selling_price_excl,
                 discount_type, discount_value, purchase_price, gst_percent,
                 taxable_amount, gst_amount, total_amount)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            ");
            $stmtItem->bind_param(
                "iiissdddsdsdddd",
                $sales_invoice_id, $pi_id, $product_id, $description, $batch_no,
                $alloc_qty, $final_unit_incl, $final_unit_excl,
                $disc_type, $disc_val, $purchase_price, $gstp,
                $taxable, $gst_amt, $line_total
            );
            $stmtItem->execute();

            if ($action === 'final') {
                $conn->query("
                    UPDATE purchase_items SET available_qty = available_qty - $alloc_qty
                    WHERE id = $pi_id
                ");
                $stmtMove = $conn->prepare("
                    INSERT INTO stock_movements
                    (product_id, purchase_item_id, type, quantity, reference_id, reference_type)
                    VALUES (?, ?, 'OUT', ?, ?, 'sale')
                ");
                $stmtMove->bind_param("iidi", $product_id, $pi_id, $alloc_qty, $sales_invoice_id);
                $stmtMove->execute();
            }
        }
    }

    // Update MRP on purchase_items if submitted
    foreach ($product_ids as $i => $product_id) {
        $product_id = (int)$product_id;
        if ($product_id <= 0) continue;
        $mrp_submitted = floatval($mrp_inputs[$i] ?? 0);
        if ($mrp_submitted > 0) {
            $stmtMrp = $conn->prepare("
                UPDATE purchase_items SET mrp = ?
                WHERE product_id = ?
                  AND purchase_order_id IN (SELECT id FROM purchase_orders WHERE company_id = ?)
            ");
            $stmtMrp->bind_param("dii", $mrp_submitted, $product_id, $cid);
            $stmtMrp->execute();
        }
    }

    $net_total = $gross_total;
    if ($overall_disc_type === 'percent') $net_total -= ($gross_total * $overall_disc_val / 100);
    elseif ($overall_disc_type === 'amount') $net_total -= $overall_disc_val;
    if ($net_total < 0) $net_total = 0;

    $discount_ratio = ($gross_total > 0) ? ($net_total / $gross_total) : 1;
    $total_taxable  *= $discount_ratio;
    $total_gst      *= $discount_ratio;

    $net_total = round($net_total + $round_off, 2);
    // Balance recalculated from payment ledger
    $paid_sum       = (float)$conn->query("SELECT COALESCE(SUM(amount),0) AS t FROM invoice_payments WHERE sales_invoice_id=$sales_invoice_id")->fetch_assoc()['t'];
    $balance_amount = max(0, round($net_total - $paid_sum, 2));
    $status         = ($action === 'final') ? 'final' : 'draft';

    $stmtUpd = $conn->prepare("
        UPDATE sales_invoices
        SET invoice_no=?, client_id=?, invoice_date=?, gross_total=?,
            overall_discount_type=?, overall_discount_value=?,
            total_taxable=?, total_gst=?, net_total=?, round_off=?,
            received_amount=?, balance_amount=?, status=?
        WHERE id=?
    ");
    $stmtUpd->bind_param(
        "sisdsdddddddsi",
        $invoice_no, $client_id, $invoice_date, $gross_total,
        $overall_disc_type, $overall_disc_val,
        $total_taxable, $total_gst, $net_total, $round_off,
        $paid_sum, $balance_amount, $status,
        $sales_invoice_id
    );
    $stmtUpd->execute();

    $conn->commit();

    if ($status === 'final') {
        header("Location: view.php?id=" . $sales_invoice_id);
    } else {
        header("Location: edit.php?id=" . $sales_invoice_id);
    }
    exit;

} catch (Exception $e) {
    $conn->rollback();
    die("Update Failed: " . $e->getMessage());
}
