<?php
class BillingEngine {
    private $db;

    public function __construct($dbConnection) {
        $this->db = $dbConnection;
    }

    /**
     * Finds an active unpaid invoice or creates a new one for the patient.
     */
    private function getActiveInvoice($patientId, $encounterId) {
        $stmt = $this->db->prepare("SELECT id FROM billing_invoices WHERE patient_id = ? AND status IN ('draft', 'unpaid', 'partial') LIMIT 1");
        $stmt->execute([$patientId]);
        $invoice = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($invoice) {
            return $invoice['id'];
        }

        $stmt = $this->db->prepare("INSERT INTO billing_invoices (patient_id, encounter_id, status) VALUES (?, ?, 'unpaid')");
        $stmt->execute([$patientId, $encounterId]);
        return $this->db->lastInsertId();
    }

    /**
     * The universal method for ANY department to add a charge.
     */
    public function addCharge($patientId, $encounterId, $department, $itemCode, $description, $qty, $unitPrice, $metadata = []) {
        try {
            $this->db->beginTransaction();

            $invoiceId = $this->getActiveInvoice($patientId, $encounterId);
            $totalPrice = $qty * $unitPrice;
            $jsonMeta = empty($metadata) ? null : json_encode($metadata);

            // Insert the line item
            $stmt = $this->db->prepare("INSERT INTO billing_items (invoice_id, department, item_code, description, quantity, unit_price, total_price, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
            $stmt->execute([$invoiceId, $department, $itemCode, $description, $qty, $unitPrice, $totalPrice, $jsonMeta]);

            $this->recalculateInvoiceTotal($invoiceId);
            
            $this->db->commit();
            return ["status" => "success", "invoice_id" => $invoiceId];

        } catch (Exception $e) {
            $this->db->rollBack();
            return ["status" => "error", "message" => $e->getMessage()];
        }
    }

    /**
     * Processes a payment and automatically updates invoice status.
     */
    public function processPayment($invoiceId, $amount, $method, $transactionRef, $staffId) {
        try {
            $this->db->beginTransaction();

            // Record payment
            $stmt = $this->db->prepare("INSERT INTO billing_payments (invoice_id, amount, payment_method, transaction_ref, processed_by) VALUES (?, ?, ?, ?, ?)");
            $stmt->execute([$invoiceId, $amount, $method, $transactionRef, $staffId]);

            // Update invoice amount_paid
            $stmt = $this->db->prepare("UPDATE billing_invoices SET amount_paid = amount_paid + ? WHERE id = ?");
            $stmt->execute([$amount, $invoiceId]);

            $this->updateInvoiceStatus($invoiceId);

            $this->db->commit();
            return ["status" => "success", "message" => "Payment applied successfully."];

        } catch (Exception $e) {
            $this->db->rollBack();
            return ["status" => "error", "message" => $e->getMessage()];
        }
    }

    private function recalculateInvoiceTotal($invoiceId) {
        $stmt = $this->db->prepare("UPDATE billing_invoices SET subtotal = (SELECT SUM(total_price) FROM billing_items WHERE invoice_id = ?), total_amount = (SELECT SUM(total_price) FROM billing_items WHERE invoice_id = ?) - discount WHERE id = ?");
        $stmt->execute([$invoiceId, $invoiceId, $invoiceId]);
    }

    private function updateInvoiceStatus($invoiceId) {
        $stmt = $this->db->prepare("SELECT total_amount, amount_paid FROM billing_invoices WHERE id = ?");
        $stmt->execute([$invoiceId]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        $status = 'unpaid';
        if ($row['amount_paid'] >= $row['total_amount']) {
            $status = 'paid';
        } elseif ($row['amount_paid'] > 0) {
            $status = 'partial';
        }

        $stmt = $this->db->prepare("UPDATE billing_invoices SET status = ? WHERE id = ?");
        $stmt->execute([$status, $invoiceId]);
    }
}
?>