<?php
class Receipts extends Model
{
    /** Receipt types & display metadata. */
    public static function TYPES(): array
    {
        return [
            'EFD_VAT'     => ['label' => 'EFD · VAT',     'icon' => 'fas fa-receipt',        'requires_efd' => true,  'has_vat' => true],
            'EFD_NON_VAT' => ['label' => 'EFD · Non-VAT', 'icon' => 'fas fa-file-invoice',   'requires_efd' => true,  'has_vat' => false],
            'NORMAL'      => ['label' => 'Normal (no EFD)','icon' => 'fas fa-money-check',    'requires_efd' => false, 'has_vat' => false],
        ];
    }

    public function nextReceiptNo(): string
    {
        if (!class_exists('DocNumberSettings')) require_once ROOT . '/app/models/DocNumberSettings.php';
        return (new DocNumberSettings())->allocate('RECEIPT');
    }

    /**
     * Record a customer receipt against a sale.
     * $d = [ 'sale_id' => int, 'amount' => float, 'currency' => 'TZS',
     *        'bank_account_id' => int, 'payment_method' => '...', 'bank_reference' => '...',
     *        'receipt_date' => 'YYYY-MM-DD',
     *        'receipt_type' => 'EFD_VAT|EFD_NON_VAT|NORMAL',
     *        'efd_device_id' => int, 'efd_receipt_number' => '...', 'efd_z_number' => '...',
     *        'efd_signature' => '...', 'efd_issued_at' => 'YYYY-MM-DD HH:MM:SS',
     *        'narrative' => '...' ]
     */
    public function record(array $d, int $uid, string $uname): array
    {
        $types = self::TYPES();
        $rtype = (string)($d['receipt_type'] ?? 'NORMAL');
        if (!isset($types[$rtype])) return ['ok' => false, 'msg' => 'Invalid receipt_type.', 'id' => 0];

        $amt = round((float)($d['amount'] ?? 0), 2);
        if ($amt <= 0) return ['ok' => false, 'msg' => 'Amount must be > 0.', 'id' => 0];

        $bankId = (int)($d['bank_account_id'] ?? 0);
        if ($bankId <= 0) return ['ok' => false, 'msg' => 'Bank account is required.', 'id' => 0];

        $sid = (int)($d['sale_id'] ?? 0);
        $db  = new DB();

        // Resolve the sale (if any) — capture customer + project denorm
        $custId = 0; $custNm = ''; $prjId = 0; $currency = (string)($d['currency'] ?? 'TZS');
        if ($sid > 0) {
            $srs = $db->mySelect(
                "SELECT sale_no, invoice_ref, customer_id, customer_name, project_id, currency, total, amount_paid, invoice_workflow_status
                   FROM sales WHERE c0d = :id AND deleted = 0",
                [':id' => $sid]
            );
            $s = $srs[0] ?? null;
            if (!$s) return ['ok' => false, 'msg' => 'Linked sale not found.', 'id' => 0];
            if ($s['invoice_workflow_status'] !== 'ISSUED') {
                return ['ok' => false, 'msg' => 'Sale is not ISSUED (status=' . $s['invoice_workflow_status'] . ').', 'id' => 0];
            }
            $balance = round((float)$s['total'] - (float)$s['amount_paid'], 2);
            if ($amt > $balance + 0.01) {
                return ['ok' => false, 'msg' => 'Receipt amount (' . number_format($amt) . ') exceeds outstanding balance (' . number_format($balance) . ').', 'id' => 0];
            }
            $custId = (int)$s['customer_id']; $custNm = (string)$s['customer_name'];
            $prjId  = (int)$s['project_id'];  $currency = (string)$s['currency'];
        } else {
            $custId = (int)($d['customer_id'] ?? 0);
            $custNm = (string)($d['customer_name'] ?? '');
        }

        // EFD validation ---------------------------------------------------
        $efdDeviceId = (int)($d['efd_device_id'] ?? 0);
        $efdRcpNo    = trim((string)($d['efd_receipt_number'] ?? ''));
        $efdZ        = trim((string)($d['efd_z_number']       ?? ''));
        $efdSig      = trim((string)($d['efd_signature']      ?? ''));
        $efdAt       = (string)($d['efd_issued_at'] ?? '');
        $efdSerial   = '';
        if ($types[$rtype]['requires_efd']) {
            if ($efdDeviceId <= 0) return ['ok' => false, 'msg' => 'EFD device is required for ' . $types[$rtype]['label'] . '.', 'id' => 0];
            if ($efdRcpNo === '')  return ['ok' => false, 'msg' => 'EFD receipt number is required for ' . $types[$rtype]['label'] . '.', 'id' => 0];
            $drs = $db->mySelect("SELECT device_serial FROM efd_devices WHERE c0d = :id AND is_active = 1", [':id' => $efdDeviceId]);
            if (empty($drs)) return ['ok' => false, 'msg' => 'EFD device not found or inactive.', 'id' => 0];
            $efdSerial = (string)$drs[0]['device_serial'];
        } else {
            $efdDeviceId = 0; $efdRcpNo = ''; $efdZ = ''; $efdSig = ''; $efdAt = ''; $efdSerial = '';
        }

        // Compute EFD output VAT (portion of amount that is VAT) -----------
        $efdVat = 0.00;
        if ($types[$rtype]['has_vat'] && $sid > 0) {
            $vrs = $db->mySelect("SELECT vat_rate, total, vat_amount FROM sales WHERE c0d = :id", [':id' => $sid]);
            if (!empty($vrs)) {
                $vRate = (float)$vrs[0]['vat_rate'];
                // VAT portion of THIS receipt = amt × vat_rate / (100 + vat_rate)
                $efdVat = round($amt * $vRate / (100 + $vRate), 2);
            }
        }

        $rcp = $this->nextReceiptNo();
        $r = $db->insertUpdate(
            "INSERT INTO receipts
                (receipt_no, receipt_type, sale_id, customer_id, customer_name, project_id,
                 amount, currency, bank_account_id, payment_method, bank_reference, receipt_date,
                 efd_device_id, efd_serial, efd_receipt_number, efd_z_number, efd_signature, efd_issued_at, efd_output_vat,
                 narrative, received_by, received_by_name, created_by, edited_by)
             VALUES
                (:rn, :rt, :sid, :cid, :cnm, :pid,
                 :amt, :cur, :ba, :pm, :br, :rd,
                 :edi, :esr, :ern, :ez, :es, :eia, :ev,
                 :n, :uid, :unm, :uid, :uid)",
            [
                ':rn'  => $rcp,
                ':rt'  => $rtype,
                ':sid' => $sid,
                ':cid' => $custId,
                ':cnm' => $custNm,
                ':pid' => $prjId,
                ':amt' => $amt,
                ':cur' => $currency,
                ':ba'  => $bankId,
                ':pm'  => (string)($d['payment_method'] ?? 'BANK_TRANSFER'),
                ':br'  => (string)($d['bank_reference'] ?? ''),
                ':rd'  => (string)($d['receipt_date'] ?? date('Y-m-d')),
                ':edi' => $efdDeviceId,
                ':esr' => $efdSerial,
                ':ern' => $efdRcpNo,
                ':ez'  => $efdZ,
                ':es'  => $efdSig,
                ':eia' => $efdAt !== '' ? $efdAt : null,
                ':ev'  => $efdVat,
                ':n'   => (string)($d['narrative'] ?? ''),
                ':uid' => $uid,
                ':unm' => $uname,
            ]
        );
        $rid = (int)$r['last_id'];
        if ($rid <= 0) return ['ok' => false, 'msg' => 'Save failed.', 'id' => 0];

        // Auto-post the money-in JE (Dr Bank / Cr 1121 AR)
        $jeMsg = '';
        $jeId  = 0;
        if (class_exists('JournalPosting')) {
            $jp   = new JournalPosting();
            $jres = $jp->postForReceipt($rid);
            $jeMsg = $jres['ok'] ? ' · ' . $jres['msg'] : ' · Journal WARNING: ' . ($jres['msg'] ?? 'post failed');
            if ($jres['ok']) {
                $jeId = (int)($jres['id'] ?? 0);
                $db->insertUpdate("UPDATE receipts SET journal_entry_id = :je WHERE c0d = :id", [':je' => $jeId, ':id' => $rid]);
            }
        }

        // Update sales.amount_paid
        if ($sid > 0) {
            $db->insertUpdate(
                "UPDATE sales SET amount_paid = amount_paid + :amt, edited_on = NOW() WHERE c0d = :id",
                [':amt' => $amt, ':id' => $sid]
            );
        }

        return ['ok' => true, 'msg' => 'Receipt recorded (' . $rcp . ').' . $jeMsg, 'id' => $rid, 'receipt_no' => $rcp, 'journal_entry_id' => $jeId];
    }

    public function byId(int $id): array
    {
        $db = new DB();
        $rs = $db->mySelect(
            "SELECT r.*, s.sale_no, s.invoice_ref, s.total AS sale_total,
                    p.project_no, ba.bank_name, ba.account_name AS bank_account_name,
                    d.device_model
               FROM receipts r
          LEFT JOIN sales s          ON s.c0d = r.sale_id
          LEFT JOIN c_projects p     ON p.c0d = r.project_id
          LEFT JOIN bank_accounts ba ON ba.c0d = r.bank_account_id
          LEFT JOIN efd_devices d    ON d.c0d = r.efd_device_id
              WHERE r.c0d = :id AND r.deleted = 0 LIMIT 1",
            [':id' => $id]
        );
        return $rs[0] ?? [];
    }

    public function list(string $receipt_type = '', int $projectId = 0): array
    {
        $db     = new DB();
        $where  = "WHERE r.deleted = 0";
        $params = [];
        if ($receipt_type !== '') { $where .= " AND r.receipt_type = :t";      $params[':t'] = $receipt_type; }
        if ($projectId > 0)       { $where .= " AND s.project_id = :p";        $params[':p'] = $projectId; }
        return $db->mySelect(
            "SELECT r.c0d, r.receipt_no, r.receipt_type, r.customer_name, r.amount, r.currency,
                    r.efd_receipt_number, r.efd_serial, r.efd_output_vat,
                    r.receipt_date, r.payment_method, r.bank_reference,
                    s.sale_no, s.invoice_ref
               FROM receipts r
          LEFT JOIN sales s ON s.c0d = r.sale_id
               $where
              ORDER BY r.c0d DESC",
            $params
        ) ?: [];
    }

    public function forSale(int $sale_id): array
    {
        $db = new DB();
        return $db->mySelect(
            "SELECT c0d, receipt_no, receipt_type, amount, currency, receipt_date, payment_method,
                    bank_reference, efd_receipt_number, efd_serial, efd_output_vat, journal_entry_id
               FROM receipts WHERE sale_id = :id AND deleted = 0 ORDER BY c0d DESC",
            [':id' => $sale_id]
        ) ?: [];
    }
}
