<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);

require_once __DIR__ . '/../../../includes/auth.php';
requireLogin();

require_once __DIR__ . '/../../../config/database.php';
require_once __DIR__ . '/../engine/calculate_block_bill.php';

/* ===============================
   GENERATE INVOICE NUMBER
================================ */
function generateInvoiceNumber(PDO $pdo, int $companyId): string
{
    $ym = date('Y-m');

    $stmt = $pdo->prepare("
        SELECT last_seq
        FROM invoice_counters
        WHERE company_id = ? AND ym = ?
        FOR UPDATE
    ");
    $stmt->execute([$companyId, $ym]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$row) {
        $sequence = 1;

        $pdo->prepare("
            INSERT INTO invoice_counters (company_id, ym, last_seq)
            VALUES (?, ?, ?)
        ")->execute([$companyId, $ym, $sequence]);

    } else {
        $sequence = (int)$row['last_seq'] + 1;

        $pdo->prepare("
            UPDATE invoice_counters
            SET last_seq = ?
            WHERE company_id = ? AND ym = ?
        ")->execute([$sequence, $companyId, $ym]);
    }

    return 'INV-' . date('Ym') . '-' . str_pad($sequence, 4, '0', STR_PAD_LEFT);
}

/* ===============================
   CORRECT CONSUMPTION
================================ */
function getConsumptionForPeriod(PDO $pdo, int $meterId, string $start, string $end): ?float
{
    $stmt = $pdo->prepare("
        SELECT reading_value
        FROM meter_readings
        WHERE meter_id = ?
        AND reading_date < ?
        ORDER BY reading_date DESC, id DESC
        LIMIT 1
    ");
    $stmt->execute([$meterId, $start]);
    $startReading = $stmt->fetchColumn();

    $stmt = $pdo->prepare("
        SELECT reading_value
        FROM meter_readings
        WHERE meter_id = ?
        AND reading_date <= ?
        ORDER BY reading_date DESC, id DESC
        LIMIT 1
    ");
    $stmt->execute([$meterId, $end]);
    $endReading = $stmt->fetchColumn();

    if ($startReading === false || $endReading === false) {
        return null;
    }

    $usage = (float)$endReading - (float)$startReading;

    if ($usage < 0) {
        return null;
    }

    return $usage;
}

/* ===============================
   INSERT LINE
================================ */
function insertLine(PDO $pdo, int $invoiceId, string $desc, float $qty, float $rate, float $total): void
{
    $stmt = $pdo->prepare("
        INSERT INTO invoice_lines
        (invoice_id, description, quantity, unit_price, total)
        VALUES (?,?,?,?,?)
    ");
    $stmt->execute([$invoiceId, $desc, $qty, $rate, $total]);
}

/* ===============================
   GET SUBTOTAL
================================ */
function getSubtotal(PDO $pdo, int $invoiceId): float
{
    $stmt = $pdo->prepare("
        SELECT COALESCE(SUM(total),0)
        FROM invoice_lines
        WHERE invoice_id = ?
    ");
    $stmt->execute([$invoiceId]);
    return (float)$stmt->fetchColumn();
}

/* ===============================
   BILLING MODE RESOLVER
================================ */
function resolveBillingSelection(array $post): array
{
    $mode = $post['billing_mode'] ?? 'full';

    $includeMeter   = false;
    $includeUnit    = false;
    $includeAccount = false;

    if ($mode === 'full') {
        $includeMeter   = true;
        $includeUnit    = true;
        $includeAccount = true;
    } elseif ($mode === 'rental') {
        $includeMeter   = false;
        $includeUnit    = true;
        $includeAccount = true;
    } elseif ($mode === 'utilities') {
        $includeMeter   = true;
        $includeUnit    = false;
        $includeAccount = false;
    } elseif ($mode === 'custom') {
        $includeMeter   = isset($post['include_meter']);
        $includeUnit    = isset($post['include_unit']);
        $includeAccount = isset($post['include_account']);
    }

    return [
        'mode'            => $mode,
        'include_meter'   => $includeMeter,
        'include_unit'    => $includeUnit,
        'include_account' => $includeAccount,
    ];
}

/* ===============================
   LOAD PERIOD
================================ */
$period = $pdo->query("
SELECT *
FROM billing_periods
WHERE status = 'OPEN'
LIMIT 1
")->fetch(PDO::FETCH_ASSOC);

if (!$period) {
    die("No billing period");
}

$start = $period['period_start'];
$end   = $period['period_end'];

$vatRate = 0.15;

// ===============================
// STEP FLOW CONTROL
// ===============================

// STEP 1: Initial screen
if (!isset($_GET['step']) && !isset($_POST['run_billing'])) {

    require_once __DIR__ . '/../../../includes/layout.php';
    ?>

    <div class="container">
        <div class="page-header">
            <h2>Billing Runs</h2>
            <p class="page-subtitle">
                Run monthly billing for current open period
            </p>
        </div>

        <div class="stat-card">
            <a href="?step=select" class="btn btn-primary">
                ⚡ Run Billing
            </a>
        </div>
    </div>

    <?php
    require_once __DIR__ . '/../../../includes/layout_footer.php';
    exit;
}


// STEP 2: Show billing mode selection
if (isset($_GET['step']) && $_GET['step'] === 'select' && !isset($_POST['run_billing'])) {

    require_once __DIR__ . '/../../../includes/layout.php';
    ?>

    <div class="container">

        <div class="page-header">
            <h2>Select Billing Mode</h2>
            <p class="page-subtitle">
                Choose what to include in this billing run
            </p>
        </div>

        <div class="stat-card">

            <form method="POST">

                <label style="display:block;margin-bottom:8px;">
                    <input type="radio" name="billing_mode" value="full" checked>
                    Full Billing (Meter + Unit + Account)
                </label>

                <label style="display:block;margin-bottom:8px;">
                    <input type="radio" name="billing_mode" value="rental">
                    Rental Only (Unit + Account)
                </label>

                <label style="display:block;margin-bottom:8px;">
                    <input type="radio" name="billing_mode" value="utilities">
                    Utilities Only (Meter)
                </label>

                <label style="display:block;margin-bottom:14px;">
                    <input type="radio" name="billing_mode" value="custom">
                    Custom Selection
                </label>

                <div style="padding:12px;border:1px solid #e5e7eb;border-radius:10px;margin-bottom:15px;">
                    <label style="display:block;margin-bottom:8px;">
                        <input type="checkbox" name="include_meter" value="1">
                        Include Meter
                    </label>

                    <label style="display:block;margin-bottom:8px;">
                        <input type="checkbox" name="include_unit" value="1">
                        Include Unit
                    </label>

                    <label style="display:block;">
                        <input type="checkbox" name="include_account" value="1">
                        Include Account
                    </label>
                </div>

                <button type="submit" name="run_billing" value="1" class="btn btn-primary">
                    Run Billing
                </button>

            </form>

        </div>

    </div>

    <?php
    require_once __DIR__ . '/../../../includes/layout_footer.php';
    exit;
}


// STEP 3: Billing execution only
if (!isset($_POST['run_billing'])) {
    header('Location: ?step=select');
    exit;
}

$selection = resolveBillingSelection($_POST);

$includeMeter   = $selection['include_meter'];
$includeUnit    = $selection['include_unit'];
$includeAccount = $selection['include_account'];
$billingMode    = $selection['mode'];

if (!$includeMeter && !$includeUnit && !$includeAccount) {
    die("Please select at least one billing component.");
}

$created = 0;
$skipped = 0;

/* ===============================
   LOAD ACCOUNTS
================================ */
$accounts = $pdo->query("
SELECT ba.*, s.company_id
FROM billing_accounts ba
JOIN structures s ON s.id = ba.structure_id
WHERE ba.structure_id IS NOT NULL
")->fetchAll(PDO::FETCH_ASSOC);

/* ===============================
   MAIN LOOP
================================ */
foreach ($accounts as $acc) {

    $accountId = (int)$acc['id'];
    $companyId = (int)$acc['company_id'];

    $units = [];

    /* --------------------------------
       GET UNITS FROM METERS IF NEEDED
    -------------------------------- */
    if ($includeMeter || $includeUnit) {
        $unitsStmt = $pdo->prepare("
            SELECT DISTINCT unit_name
            FROM operational_meters
            WHERE account_id = ?
            AND status = 'ACTIVE'
            AND unit_name IS NOT NULL
            AND unit_name != ''
        ");
        $unitsStmt->execute([$accountId]);
        $meterUnits = $unitsStmt->fetchAll(PDO::FETCH_COLUMN);

        foreach ($meterUnits as $u) {
            $units[] = $u;
        }
    }

    /* --------------------------------
       GET UNITS FROM UNIT CHARGES
       so unit-only billing can work
    -------------------------------- */
    if ($includeUnit) {
        $unitChargeStmt = $pdo->prepare("
            SELECT DISTINCT uc.unit_name
            FROM unit_charges uc
            JOIN billing_charges bc ON bc.id = uc.charge_id
            WHERE uc.account_id = ?
            AND uc.unit_name IS NOT NULL
            AND uc.unit_name != ''
            AND bc.applies_to = 'UNIT'
            AND bc.active = 1
        ");
        $unitChargeStmt->execute([$accountId]);
        $chargeUnits = $unitChargeStmt->fetchAll(PDO::FETCH_COLUMN);

        foreach ($chargeUnits as $u) {
            $units[] = $u;
        }
    }

    $units = array_values(array_unique(array_filter($units)));

    /* --------------------------------
       ACCOUNT-ONLY FALLBACK
    -------------------------------- */
    if (empty($units) && $includeAccount && !$includeMeter && !$includeUnit) {
        $units = ['ACCOUNT'];
    }

    if (empty($units)) {
        $skipped++;
        continue;
    }

    foreach ($units as $unitName) {

        $check = $pdo->prepare("
            SELECT id
            FROM invoices
            WHERE billing_account_id = ?
            AND unit_name = ?
            AND period_start = ?
            AND period_end = ?
        ");
        $check->execute([$accountId, $unitName, $start, $end]);

        if ($check->fetch()) {
            continue;
        }

        $meters = [];

        if ($includeMeter && $unitName !== 'ACCOUNT') {
            $metersStmt = $pdo->prepare("
                SELECT *
                FROM operational_meters
                WHERE account_id = ?
                AND unit_name = ?
                AND status = 'ACTIVE'
            ");
            $metersStmt->execute([$accountId, $unitName]);
            $meters = $metersStmt->fetchAll(PDO::FETCH_ASSOC);
        }

        $pdo->beginTransaction();

        try {

            $invoiceNumber = generateInvoiceNumber($pdo, $companyId);

            $pdo->prepare("
                INSERT INTO invoices
                (company_id, billing_account_id, unit_name, invoice_number, invoice_date, due_date, period_start, period_end, status, vat_rate)
                VALUES (?,?,?,?,?,?,?,?,?,?)
            ")->execute([
                $companyId,
                $accountId,
                $unitName,
                $invoiceNumber,
                date('Y-m-d'),
                date('Y-m-d', strtotime('+14 days')),
                $start,
                $end,
                'ISSUED',
                $vatRate
            ]);

            $invoiceId = (int)$pdo->lastInsertId();
            $hasLines = false;

            /* ===============================
               PER-METER BILLING
            ============================== */
            if ($includeMeter && !empty($meters)) {
                foreach ($meters as $m) {

                    $meterId     = (int)$m['id'];
                    $meterNumber = $m['meter_number'];
                    $tariffId    = (int)$m['tariff_id'];

                    if (!$tariffId) {
                        continue;
                    }

                    $usage = getConsumptionForPeriod($pdo, $meterId, $start, $end);

                    if ($usage === null) {
                        continue;
                    }

                    insertLine($pdo, $invoiceId, "METER_HEADER::{$meterNumber}", 0, 0, 0);

                    $result = calculateBlockBill($pdo, $tariffId, $usage);

                    foreach ($result['breakdown'] as $row) {
                        insertLine(
                            $pdo,
                            $invoiceId,
                            $row['label'],
                            (float)$row['units'],
                            (float)$row['rate'],
                            (float)$row['amount']
                        );

                        $hasLines = true;
                    }

                    /* METER CHARGES */
                    $meterCharges = $pdo->prepare("
                        SELECT bc.*
                        FROM meter_charges mc
                        JOIN billing_charges bc ON bc.id = mc.charge_id
                        WHERE mc.meter_id = ?
                        AND bc.applies_to = 'METER'
                        AND bc.active = 1
                    ");
                    $meterCharges->execute([$meterId]);

                    foreach ($meterCharges as $c) {

                        $amount = $c['charge_type'] === 'percentage'
                            ? ($usage * ((float)$c['charge_value'] / 100))
                            : (float)$c['charge_value'];

                        insertLine(
                            $pdo,
                            $invoiceId,
                            "METER_CHARGE::{$meterNumber}::" . $c['charge_name'],
                            1,
                            $amount,
                            $amount
                        );

                        $hasLines = true;
                    }
                }
            }

            /* ===============================
               UNIT CHARGES
            ============================== */
            if ($includeUnit && $unitName !== 'ACCOUNT') {
                $unitCharges = $pdo->prepare("
                    SELECT bc.*
                    FROM unit_charges uc
                    JOIN billing_charges bc ON bc.id = uc.charge_id
                    WHERE uc.account_id = ?
                    AND uc.unit_name = ?
                    AND bc.applies_to = 'UNIT'
                    AND bc.active = 1
                ");
                $unitCharges->execute([$accountId, $unitName]);

                foreach ($unitCharges as $c) {

                    $subtotal = getSubtotal($pdo, $invoiceId);

                    $amount = $c['charge_type'] === 'percentage'
                        ? ($subtotal * ((float)$c['charge_value'] / 100))
                        : (float)$c['charge_value'];

                    insertLine(
                        $pdo,
                        $invoiceId,
                        "UNIT_CHARGE::" . $c['charge_name'],
                        1,
                        $amount,
                        $amount
                    );

                    $hasLines = true;
                }
            }

            /* ===============================
               ACCOUNT CHARGES
            ============================== */
            if ($includeAccount) {
                $accountCharges = $pdo->prepare("
                    SELECT bc.*
                    FROM account_charges ac
                    JOIN billing_charges bc ON bc.id = ac.charge_id
                    WHERE ac.account_id = ?
                    AND ac.active = 1
                    AND (ac.end_date IS NULL OR ac.end_date = '0000-00-00' OR ac.end_date >= ?)
                    AND bc.applies_to = 'ACCOUNT'
                    AND bc.active = 1
                ");
                $accountCharges->execute([$accountId, $end]);

                foreach ($accountCharges as $c) {

                    $subtotal = getSubtotal($pdo, $invoiceId);

                    $amount = $c['charge_type'] === 'percentage'
                        ? ($subtotal * ((float)$c['charge_value'] / 100))
                        : (float)$c['charge_value'];

                    insertLine(
                        $pdo,
                        $invoiceId,
                        "ACCOUNT_CHARGE::" . $c['charge_name'],
                        1,
                        $amount,
                        $amount
                    );

                    $hasLines = true;
                }
            }

            if (!$hasLines) {
                $pdo->prepare("DELETE FROM invoices WHERE id = ?")->execute([$invoiceId]);
                $pdo->commit();
                continue;
            }

            $subtotal = getSubtotal($pdo, $invoiceId);
            $vat      = $subtotal * $vatRate;
            $total    = $subtotal + $vat;

            $pdo->prepare("
                UPDATE invoices
                SET subtotal = ?, vat_amount = ?, total = ?
                WHERE id = ?
            ")->execute([$subtotal, $vat, $total, $invoiceId]);

            $pdo->commit();
            $created++;

        } catch (Exception $e) {
            $pdo->rollBack();
            $skipped++;
            echo "Error on account {$accountId}, unit {$unitName}: " . htmlspecialchars($e->getMessage()) . "<br>";
        }
    }
}

require_once __DIR__ . '/../../../includes/layout.php';
?>

<div class="container">
    <div class="page-header">
        <h2>Billing Complete</h2>
        <p class="page-subtitle">
            Mode:
            <strong><?= htmlspecialchars(strtoupper($billingMode)) ?></strong>
        </p>
    </div>

    <div class="stat-card">
        <p><strong>Created:</strong> <?= (int)$created ?></p>
        <p><strong>Skipped:</strong> <?= (int)$skipped ?></p>
        <p><strong>Period:</strong> <?= htmlspecialchars($start) ?> to <?= htmlspecialchars($end) ?></p>
    </div>
</div>

<?php require_once __DIR__ . '/../../../includes/layout_footer.php'; ?>