<?php

require_once 'Logger.php';

class Quotation {
    // Matches the DECIMAL(15,2) ceiling on quotations.subtotal/surtax/total_amount
    // and quotation_items.unit_price (database_migrations/2026_07_08_widen_quotation_invoice_amounts_and_draft_status.sql).
    const MAX_AMOUNT = 9999999999999.99;

    private $conn;
    private $logger;

    public function __construct($conn, $logger = null) {
        $this->conn = $conn;
        $this->logger = $logger;
    }

    public function createQuotation($branchId, $createdBy, $quotationNumber, $customerName, $customerContact, $items, $subtotal, $surtax, $total, $validityDays, $notes = '') {
        try {
            if (empty($quotationNumber) || empty($customerName) || empty($customerContact) || empty($items) || !is_array($items)) {
                throw new Exception("Invalid input data: required fields are missing or invalid.");
            }
            if ($subtotal < 0 || $surtax < 0 || $total < 0 || $validityDays <= 0) {
                throw new Exception("Invalid numeric values: subtotal, surtax, total, or validity days must be positive.");
            }
            if ($subtotal > self::MAX_AMOUNT || $surtax > self::MAX_AMOUNT || $total > self::MAX_AMOUNT) {
                throw new Exception("Total amount MWK " . number_format($total, 2) . " exceeds the maximum allowed value of MWK " . number_format(self::MAX_AMOUNT, 2) . ".");
            }
            foreach ($items as $item) {
                if (isset($item['unit_price']) && $item['unit_price'] > self::MAX_AMOUNT) {
                    throw new Exception("Unit price for '{$item['item_name']}' exceeds the maximum allowed value of MWK " . number_format(self::MAX_AMOUNT, 2) . ".");
                }
            }

            $this->conn->beginTransaction();

            $validUntil = date('Y-m-d', strtotime("+$validityDays days"));

            $sql = "INSERT INTO quotations (quotation_number, branch_id, created_by, customer_name,
                    customer_contact, subtotal, surtax, total_amount, valid_until, notes, created_at)
                    VALUES (:quotation_number, :branch_id, :created_by, :customer_name,
                    :customer_contact, :subtotal, :surtax, :total_amount, :valid_until, :notes, NOW())";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':quotation_number', $quotationNumber);
            $stmt->bindParam(':branch_id', $branchId, PDO::PARAM_INT);
            $stmt->bindParam(':created_by', $createdBy, PDO::PARAM_INT);
            $stmt->bindParam(':customer_name', $customerName);
            $stmt->bindParam(':customer_contact', $customerContact);
            $stmt->bindParam(':subtotal', $subtotal, PDO::PARAM_STR);
            $stmt->bindParam(':surtax', $surtax, PDO::PARAM_STR);
            $stmt->bindParam(':total_amount', $total, PDO::PARAM_STR);
            $stmt->bindParam(':valid_until', $validUntil);
            $stmt->bindParam(':notes', $notes);

            if (!$stmt->execute()) {
                throw new Exception("Failed to create quotation header: database error.");
            }

            $quotationId = $this->conn->lastInsertId();

            $itemSql = "INSERT INTO quotation_items (quotation_id, item_id, unique_code, item_name, unit_price, quantity)
                        VALUES (:quotation_id, NULL, NULL, :item_name, :unit_price, :quantity)";
            $itemStmt = $this->conn->prepare($itemSql);

            foreach ($items as $item) {
                if (!isset($item['qty']) || !isset($item['item_name']) || !isset($item['unit_price'])) {
                    throw new Exception("Invalid item data: missing qty, item_name, or unit_price.");
                }
                if ($item['qty'] <= 0 || $item['unit_price'] <= 0 || empty($item['item_name'])) {
                    throw new Exception("Invalid item data: qty and unit_price must be positive, and item_name must not be empty.");
                }

                $itemStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
                $itemStmt->bindParam(':item_name', $item['item_name']);
                $itemStmt->bindParam(':unit_price', $item['unit_price'], PDO::PARAM_STR);
                $itemStmt->bindParam(':quantity', $item['qty'], PDO::PARAM_INT);

                if (!$itemStmt->execute()) {
                    throw new Exception("Failed to add item {$item['item_name']} to quotation: database error.");
                }
            }

            $this->conn->commit();

            if ($this->logger) {
                $quotationData = [
                    'quotation_number' => $quotationNumber,
                    'branch_id' => $branchId,
                    'created_by' => $createdBy,
                    'customer_name' => $customerName,
                    'customer_contact' => $customerContact,
                    'subtotal' => $subtotal,
                    'surtax' => $surtax,
                    'total_amount' => $total,
                    'valid_until' => $validUntil,
                    'notes' => $notes,
                    'items_count' => count($items)
                ];
                $this->logger->logQuotation('CREATE', $quotationId, $quotationData);
            }

            return [
                'success' => true,
                'quotation_id' => $quotationId,
                'message' => 'Quotation created successfully.'
            ];

        } catch (Exception $e) {
            if ($this->conn->inTransaction()) {
                $this->conn->rollBack();
            }
            error_log("Error creating quotation: " . $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }

    // Lenient counterpart to createQuotation(), used when a create attempt fails
    // (validation error or DB exception) so the user's in-progress work is never
    // lost. Tolerates partial/invalid data and clamps out-of-range numbers instead
    // of throwing, always persisting with status = 'draft' so it can be resumed
    // via edit_quotation.php.
    public function saveDraft($branchId, $createdBy, $quotationNumber, $customerName, $customerContact, $items, $subtotal, $surtax, $total, $validityDays, $notes = '') {
        try {
            $this->conn->beginTransaction();

            $customerName = $customerName !== '' ? $customerName : '(draft - customer name not provided)';
            $customerContact = $customerContact !== '' ? $customerContact : '(draft - contact not provided)';
            $validityDays = $validityDays > 0 ? $validityDays : 30;
            $subtotal = min(max((float)$subtotal, 0), self::MAX_AMOUNT);
            $surtax = min(max((float)$surtax, 0), self::MAX_AMOUNT);
            $total = min(max((float)$total, 0), self::MAX_AMOUNT);
            $validUntil = date('Y-m-d', strtotime("+$validityDays days"));

            $sql = "INSERT INTO quotations (quotation_number, branch_id, created_by, customer_name,
                    customer_contact, subtotal, surtax, total_amount, valid_until, notes, created_at, status)
                    VALUES (:quotation_number, :branch_id, :created_by, :customer_name,
                    :customer_contact, :subtotal, :surtax, :total_amount, :valid_until, :notes, NOW(), 'draft')";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':quotation_number', $quotationNumber);
            $stmt->bindParam(':branch_id', $branchId, PDO::PARAM_INT);
            $stmt->bindParam(':created_by', $createdBy, PDO::PARAM_INT);
            $stmt->bindParam(':customer_name', $customerName);
            $stmt->bindParam(':customer_contact', $customerContact);
            $stmt->bindParam(':subtotal', $subtotal, PDO::PARAM_STR);
            $stmt->bindParam(':surtax', $surtax, PDO::PARAM_STR);
            $stmt->bindParam(':total_amount', $total, PDO::PARAM_STR);
            $stmt->bindParam(':valid_until', $validUntil);
            $stmt->bindParam(':notes', $notes);
            $stmt->execute();

            $quotationId = $this->conn->lastInsertId();

            if (is_array($items) && !empty($items)) {
                $itemSql = "INSERT INTO quotation_items (quotation_id, item_id, unique_code, item_name, unit_price, quantity)
                            VALUES (:quotation_id, NULL, NULL, :item_name, :unit_price, :quantity)";
                $itemStmt = $this->conn->prepare($itemSql);

                foreach ($items as $item) {
                    $itemName = trim($item['item_name'] ?? '');
                    $unitPrice = min(max((float)($item['unit_price'] ?? 0), 0), self::MAX_AMOUNT);
                    $qty = max((int)($item['qty'] ?? 0), 0);

                    if ($itemName === '' && $unitPrice === 0.0 && $qty === 0) {
                        continue;
                    }
                    $itemName = $itemName !== '' ? $itemName : '(draft item)';

                    $itemStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
                    $itemStmt->bindParam(':item_name', $itemName);
                    $itemStmt->bindParam(':unit_price', $unitPrice, PDO::PARAM_STR);
                    $itemStmt->bindParam(':quantity', $qty, PDO::PARAM_INT);
                    $itemStmt->execute();
                }
            }

            $this->conn->commit();

            if ($this->logger) {
                $this->logger->logQuotation('DRAFT_SAVE', $quotationId, [
                    'quotation_number' => $quotationNumber,
                    'branch_id' => $branchId,
                    'created_by' => $createdBy,
                    'customer_name' => $customerName,
                    'customer_contact' => $customerContact,
                    'subtotal' => $subtotal,
                    'surtax' => $surtax,
                    'total_amount' => $total,
                    'valid_until' => $validUntil,
                    'notes' => $notes,
                    'items_count' => count($items)
                ]);
            }

            return [
                'success' => true,
                'quotation_id' => $quotationId,
                'message' => 'Quotation saved as a draft.'
            ];
        } catch (Exception $e) {
            if ($this->conn->inTransaction()) {
                $this->conn->rollBack();
            }
            error_log("Error saving quotation draft: " . $e->getMessage());
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }

    public function getQuotationById($quotationId) {
        $query = "SELECT q.quotation_id, q.quotation_number, q.created_at, q.valid_until, q.customer_name, q.customer_contact,
                     q.status, q.subtotal, q.surtax, q.total_amount, q.notes, q.branch_id, q.created_by
              FROM quotations q
              WHERE q.quotation_id = :quotation_id";
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
        $stmt->execute();
        $quotation = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($quotation) {
            $itemQuery = "SELECT item_name, unit_price, quantity FROM quotation_items WHERE quotation_id = :quotation_id";
            $itemStmt = $this->conn->prepare($itemQuery);
            $itemStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
            $itemStmt->execute();
            $quotation['items'] = $itemStmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
        } else {
            $quotation = [];
        }

        return $quotation;
    }

    public function updateQuotation($quotationId, $customerName, $customerContact, $validUntil, $status, $items, $subtotal, $surtax, $grandTotal, $notes) {
        $oldData = null;
        if ($this->logger) {
            $oldData = $this->getQuotationById($quotationId);
        }

        $this->conn->beginTransaction();

        try {
            $deleteItemsQuery = "DELETE FROM quotation_items WHERE quotation_id = :quotation_id";
            $deleteStmt = $this->conn->prepare($deleteItemsQuery);
            $deleteStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
            $deleteStmt->execute();

            $updateQuery = "UPDATE quotations
                            SET customer_name = :customer_name,
                                customer_contact = :customer_contact,
                                valid_until = :valid_until,
                                status = :status,
                                subtotal = :subtotal,
                                surtax = :surtax,
                                total_amount = :grand_total,
                                notes = :notes
                            WHERE quotation_id = :quotation_id";
            $updateStmt = $this->conn->prepare($updateQuery);
            $updateStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
            $updateStmt->bindParam(':customer_name', $customerName);
            $updateStmt->bindParam(':customer_contact', $customerContact);
            $updateStmt->bindParam(':valid_until', $validUntil);
            $updateStmt->bindParam(':status', $status);
            $updateStmt->bindParam(':subtotal', $subtotal, PDO::PARAM_STR);
            $updateStmt->bindParam(':surtax', $surtax, PDO::PARAM_STR);
            $updateStmt->bindParam(':grand_total', $grandTotal, PDO::PARAM_STR);
            $updateStmt->bindParam(':notes', $notes);
            $updateStmt->execute();

            $insertItemQuery = "INSERT INTO quotation_items (quotation_id, quantity, item_name, unit_price)
                                VALUES (:quotation_id, :quantity, :item_name, :unit_price)";
            $insertStmt = $this->conn->prepare($insertItemQuery);
            foreach ($items as $item) {
                $insertStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
                $insertStmt->bindParam(':quantity', $item['qty'], PDO::PARAM_INT);
                $insertStmt->bindParam(':item_name', $item['item_name']);
                $insertStmt->bindParam(':unit_price', $item['unit_price'], PDO::PARAM_STR);
                $insertStmt->execute();
            }

            $this->conn->commit();

            if ($this->logger && $oldData) {
                $newData = [
                    'customer_name' => $customerName,
                    'customer_contact' => $customerContact,
                    'valid_until' => $validUntil,
                    'status' => $status,
                    'subtotal' => $subtotal,
                    'surtax' => $surtax,
                    'total_amount' => $grandTotal,
                    'notes' => $notes,
                    'items_count' => count($items)
                ];
                $this->logger->logQuotation('UPDATE', $quotationId, $newData, $oldData);
            }

            return true;
        } catch (PDOException $e) {
            $this->conn->rollBack();
            error_log("Update quotation failed: " . $e->getMessage());
            return false;
        }
    }

    public function getAllQuotations() {
        $query = "SELECT q.quotation_id, q.quotation_number, q.created_at, q.valid_until, q.customer_name, q.customer_contact, q.status, q.branch_id
                FROM quotations q";
        $stmt = $this->conn->prepare($query);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getQuotationsByBranch($branchId, $startDate = null, $endDate = null) {
        try {
            $sql = "SELECT q.*, u.username as created_by_name
                    FROM quotations q
                    JOIN users u ON q.created_by = u.user_id
                    WHERE q.branch_id = :branch_id";

            if ($startDate && $endDate) {
                $sql .= " AND DATE(q.created_at) BETWEEN :start_date AND :end_date";
            }

            $sql .= " ORDER BY q.created_at DESC";

            $stmt = $this->conn->prepare($sql);
            $stmt->bindParam(':branch_id', $branchId, PDO::PARAM_INT);

            if ($startDate && $endDate) {
                $stmt->bindParam(':start_date', $startDate);
                $stmt->bindParam(':end_date', $endDate);
            }

            $stmt->execute();
            return $stmt->fetchAll(PDO::FETCH_ASSOC);

        } catch (Exception $e) {
            error_log("Error getting quotations by branch: " . $e->getMessage());
            return [];
        }
    }

    public function convertToSale($quotationId, $userId) {
        try {
            $quotation = $this->getQuotationById($quotationId);

            if (!$quotation) {
                throw new Exception("Quotation not found");
            }

            $this->conn->beginTransaction();

            $transactionId = 'SALE-' . date('Ymd') . '-' . mt_rand(1000, 9999);

            require_once __DIR__ . '/Sales.php';
            $sales = new Sales($this->conn, $this->logger);

            foreach ($quotation['items'] as $item) {
                $result = $sales->createSale(
                    date('Y-m-d H:i:s'),
                    $item['unique_code'] ?? null,
                    $item['unit_price'],
                    $item['quantity'],
                    $quotation['branch_id'],
                    $userId,
                    $transactionId
                );

                if (!$result) {
                    throw new Exception("Failed to create sale for item {$item['item_name']}");
                }
            }

            $updateSql = "UPDATE quotations SET status = 'converted', converted_at = NOW(),
                          converted_by = :user_id, transaction_id = :transaction_id
                          WHERE quotation_id = :quotation_id";
            $updateStmt = $this->conn->prepare($updateSql);
            $updateStmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
            $updateStmt->bindParam(':transaction_id', $transactionId);
            $updateStmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);

            if (!$updateStmt->execute()) {
                throw new Exception("Failed to update quotation status");
            }

            $this->conn->commit();

            if ($this->logger) {
                $conversionData = [
                    'quotation_id' => $quotationId,
                    'quotation_number' => $quotation['quotation_number'],
                    'transaction_id' => $transactionId,
                    'converted_by' => $userId,
                    'total_amount' => $quotation['total_amount'],
                    'customer_name' => $quotation['customer_name'],
                    'items_count' => count($quotation['items'])
                ];
                $this->logger->logQuotation('CONVERT_TO_SALE', $quotationId, $conversionData);
            }

            return $transactionId;

        } catch (Exception $e) {
            if ($this->conn->inTransaction()) {
                $this->conn->rollBack();
            }
            error_log("Error converting quotation to sale: " . $e->getMessage());
            return false;
        }
    }

    public function deleteQuotation($quotationId) {
        try {
            $quotationData = null;
            if ($this->logger) {
                $quotationData = $this->getQuotationById($quotationId);
            }

            $this->conn->beginTransaction();
            $queryItems = "DELETE FROM quotation_items WHERE quotation_id = :quotation_id";
            $stmtItems = $this->conn->prepare($queryItems);
            $stmtItems->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
            $stmtItems->execute();

            $query = "DELETE FROM quotations WHERE quotation_id = :quotation_id";
            $stmt = $this->conn->prepare($query);
            $stmt->bindParam(':quotation_id', $quotationId, PDO::PARAM_INT);
            $success = $stmt->execute();

            if ($success && $this->logger && $quotationData) {
                $this->logger->logQuotation('DELETE', $quotationId, null, $quotationData);
            }

            $this->conn->commit();
            return $success;
        } catch (PDOException $e) {
            $this->conn->rollBack();
            error_log("Error deleting quotation: " . $e->getMessage());
            throw new Exception("Database error: " . $e->getMessage());
        }
    }
}
?>