<?php
require_once __DIR__ . '/../../includes/bootstrap.php';
require_login();

$txId = (int)($_POST['tx_id'] ?? $_GET['tx_id'] ?? 0);
$reason = trim((string)($_POST['reason'] ?? 'Reversed by user'));

if ($txId <= 0) {
    die('Invalid transaction.');
}
if (!function_exists('write_payment_audit_log')) {
    function write_payment_audit_log(
        PDO $pdo,
        ?int $propertyId,
        ?int $orderId,
        ?int $folioId,
        ?int $paymentId,
        ?int $folioTransactionId,
        string $actionType,
        ?string $reason,
        array $payload = []
    ): void {
        $stmt = $pdo->prepare("
            INSERT INTO payment_audit_log
            (
                property_id,
                order_id,
                folio_id,
                payment_id,
                folio_transaction_id,
                action_type,
                action_reason,
                action_by,
                action_by_type,
                action_at,
                payload_json
            )
            VALUES
            (
                :property_id,
                :order_id,
                :folio_id,
                :payment_id,
                :folio_transaction_id,
                :action_type,
                :action_reason,
                :action_by,
                :action_by_type,
                NOW(),
                :payload_json
            )
        ");
        $stmt->execute([
            ':property_id'          => $propertyId ?: null,
            ':order_id'             => $orderId ?: null,
            ':folio_id'             => $folioId ?: null,
            ':payment_id'           => $paymentId ?: null,
            ':folio_transaction_id' => $folioTransactionId ?: null,
            ':action_type'          => $actionType,
            ':action_reason'        => $reason !== '' ? $reason : null,
            ':action_by'            => (int)($_SESSION['user_id'] ?? 0) ?: null,
            ':action_by_type'       => trim((string)($_SESSION['type'] ?? '')) ?: null,
            ':payload_json'         => !empty($payload) ? json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null,
        ]);
    }
}
function recalculate_folio_totals(PDO $pdo, int $folioId): void
{
    $stmt = $pdo->prepare("
        SELECT
            COALESCE(SUM(debit), 0)  AS total_debits,
            COALESCE(SUM(credit), 0) AS total_credits
        FROM folio_transactions
        WHERE folio_id = :folio_id
    ");
    $stmt->execute([':folio_id' => $folioId]);
    $totals = $stmt->fetch(PDO::FETCH_ASSOC);

    $charges  = (float)($totals['total_debits'] ?? 0);
    $payments = (float)($totals['total_credits'] ?? 0);
    $balance  = $charges - $payments;

    $stmt = $pdo->prepare("
        UPDATE folios
        SET total_charges = :total_charges,
            total_payments = :total_payments,
            balance = :balance,
            updated_at = NOW()
        WHERE id = :id
    ");
    $stmt->execute([
        ':total_charges'  => round($charges, 2),
        ':total_payments' => round($payments, 2),
        ':balance'        => round($balance, 2),
        ':id'             => $folioId,
    ]);
}

function sync_order_payment_status(PDO $pdo, int $orderId): void
{
    $stmt = $pdo->prepare("
        SELECT total_charges, total_payments, balance
        FROM folios
        WHERE order_id = :order_id
        LIMIT 1
    ");
    $stmt->execute([':order_id' => $orderId]);
    $folio = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$folio) {
        $pdo->prepare("UPDATE orders SET payment_status = 0 WHERE id = :id")
            ->execute([':id' => $orderId]);
        return;
    }

    $payments = (float)($folio['total_payments'] ?? 0);
    $balance  = (float)($folio['balance'] ?? 0);

    if ($payments <= 0.00001) {
        $status = 0;
    } elseif ($balance <= 0.00001) {
        $status = 2;
    } else {
        $status = 1;
    }

    $pdo->prepare("
        UPDATE orders
        SET payment_status = :payment_status
        WHERE id = :id
    ")->execute([
        ':payment_status' => $status,
        ':id'             => $orderId,
    ]);
}

try {
    $pdo->beginTransaction();

    $stmt = $pdo->prepare("
        SELECT *
        FROM folio_transactions
        WHERE id = :id
        LIMIT 1
    ");
    $stmt->execute([':id' => $txId]);
    $tx = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$tx) {
        throw new RuntimeException('Transaction not found.');
    }

    if ((int)($tx['is_voided'] ?? 0) === 1) {
        throw new RuntimeException('Already voided.');
    }

    $propertyId = (int)($tx['property_id'] ?? 0);

    if ($propertyId <= 0) {
        $stmtProp = $pdo->prepare("
            SELECT property_id
            FROM folios
            WHERE id = :id
            LIMIT 1
        ");
        $stmtProp->execute([':id' => (int)$tx['folio_id']]);
        $propertyId = (int)($stmtProp->fetchColumn() ?: 0);
    }

    if ($propertyId <= 0) {
        $stmtProp = $pdo->prepare("
            SELECT property_id
            FROM orders
            WHERE id = :id
            LIMIT 1
        ");
        $stmtProp->execute([':id' => (int)$tx['order_id']]);
        $propertyId = (int)($stmtProp->fetchColumn() ?: 0);
    }

    $check = $pdo->prepare("
        SELECT COUNT(*)
        FROM folio_transactions
        WHERE reversed_from_id = :id
    ");
    $check->execute([':id' => $txId]);
    if ((int)$check->fetchColumn() > 0) {
        throw new RuntimeException('Already reversed.');
    }

    $reverseDebit  = (float)$tx['credit'];
    $reverseCredit = (float)$tx['debit'];

    $insert = $pdo->prepare("
        INSERT INTO folio_transactions
        (
            property_id,
            folio_id,
            order_id,
            tx_date,
            tx_type,
            description,
            debit,
            credit,
            source_table,
            source_id,
            reversed_from_id
        )
        VALUES
        (
            :property_id,
            :folio_id,
            :order_id,
            NOW(),
            'reversal',
            :description,
            :debit,
            :credit,
            'folio_transactions',
            :source_id,
            :reversed_from_id
        )
    ");
    $insert->execute([
        ':property_id'      => $propertyId,
        ':folio_id'         => (int)$tx['folio_id'],
        ':order_id'         => (int)$tx['order_id'],
        ':description'      => 'Reversal of TX #' . (int)$tx['id'] . ' - ' . $reason,
        ':debit'            => $reverseDebit,
        ':credit'           => $reverseCredit,
        ':source_id'        => $txId,
        ':reversed_from_id' => $txId,
    ]);
$reversalTxId = (int)$pdo->lastInsertId();
    $pdo->prepare("
        UPDATE folio_transactions
        SET is_voided = 1,
            voided_at = NOW(),
            void_reason = :reason
        WHERE id = :id
    ")->execute([
        ':reason' => $reason,
        ':id'     => $txId,
    ]);
write_payment_audit_log(
    $pdo,
    $propertyId,
    (int)$tx['order_id'],
    (int)$tx['folio_id'],
    null,
    $reversalTxId,
    'reverse_folio_transaction',
    $reason,
    [
        'original_transaction' => [
            'id'          => (int)$tx['id'],
            'tx_type'     => (string)$tx['tx_type'],
            'description' => (string)$tx['description'],
            'debit'       => (float)$tx['debit'],
            'credit'      => (float)$tx['credit'],
            'source_table'=> (string)($tx['source_table'] ?? ''),
            'source_id'   => (int)($tx['source_id'] ?? 0),
        ],
        'reversal_transaction' => [
            'id'          => $reversalTxId,
            'debit'       => $reverseDebit,
            'credit'      => $reverseCredit,
            'description' => 'Reversal of TX #' . (int)$tx['id'] . ' - ' . $reason,
        ],
    ]
);
    recalculate_folio_totals($pdo, (int)$tx['folio_id']);
    sync_order_payment_status($pdo, (int)$tx['order_id']);

    $pdo->commit();

    $redirect = 'folio_view.php?order_id=' . (int)$tx['order_id'] . '&reversed=1';
    if ($propertyId > 0) {
        $redirect .= '&property_id=' . $propertyId;
    }

    header('Location: ' . $redirect);
    exit;

} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    die($e->getMessage());
}