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

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

$invoice_no   = 'QTY/' . date('YmdHis');
$invoice_date = $_POST['invoice_date'] ?? date('Y-m-d');
$client_id    = (int)$_POST['client_id'];
$action       = $_POST['action']; // save | final

$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);
$received_amount   = floatval($_POST['received_amount'] ?? 0);
$payment_mode      = $_POST['payment_mode'];

/**
 * FIFO allocation: returns array of batch allocations for the given product qty.
 * Returns [] if total available stock < qty_needed.
 */
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 []; // insufficient stock
    return $allocations;
}

/**
 * For draft saves: get the first available batch info for reference (no stock check).
 */
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 {
    if ($action === 'final') {
        $invoice_no = generateFinalInvoiceNo($client_id, $conn, $cid);
    }

    $stmt = $conn->prepare("
        INSERT INTO sales_invoices
        (invoice_no, invoice_date, client_id, status, company_id)
        VALUES (?, ?, ?, 'draft', ?)
    ");
    $stmt->bind_param("ssii", $invoice_no, $invoice_date, $client_id, $cid);
    $stmt->execute();
    $sales_invoice_id = $stmt->insert_id;

    $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] ?? '';

        // Apply per-unit discount on GST-inclusive price
        $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 {
            // Draft: record against first available batch without stock check
            $bInfo = getFirstBatch($conn, $product_id);
            if (!$bInfo) continue; // product never purchased — skip
            $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 and currently 0/null
        $mrp_submitted = floatval($mrp_inputs[$i] ?? 0);
        if ($mrp_submitted > 0) {
            $stmtMrp = $conn->prepare("
                UPDATE purchase_items SET mrp = ?
                WHERE product_id = ?
                  AND (mrp IS NULL OR mrp = 0)
                  AND purchase_order_id IN (SELECT id FROM purchase_orders WHERE company_id = ?)
            ");
            $stmtMrp->bind_param("dii", $mrp_submitted, $product_id, $cid);
            $stmtMrp->execute();
        }
    }

    // Apply overall discount
    $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_amount = max(0, $net_total - $received_amount);
    $status         = ($action === 'final') ? 'final' : 'draft';

    $stmtUpd = $conn->prepare("
        UPDATE sales_invoices
        SET invoice_no=?, gross_total=?, overall_discount_type=?, overall_discount_value=?,
            total_taxable=?, total_gst=?, net_total=?, round_off=?,
            received_amount=?, balance_amount=?, payment_mode=?, status=?
        WHERE id=?
    ");
    $stmtUpd->bind_param(
        "sdsdddddddssi",
        $invoice_no, $gross_total, $overall_disc_type, $overall_disc_val,
        $total_taxable, $total_gst, $net_total, $round_off,
        $received_amount, $balance_amount, $payment_mode, $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("Sales Save Failed: " . $e->getMessage());
}
