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

/* ---------------------------------
   Role restriction
--------------------------------- */
$allowedTypes = ['master', 'manager', 'accounts', '2'];

$currentType = strtolower(trim((string)($_SESSION['ss_role_id'] ?? '')));
if (!in_array($currentType, $allowedTypes, true)) {
    http_response_code(403);
    exit('You are not allowed to void payments.');
}

$paymentId = (int)($_POST['payment_id'] ?? $_GET['payment_id'] ?? 0);
$orderId   = (int)($_POST['order_id'] ?? $_GET['order_id'] ?? 0);
$reason    = trim((string)($_POST['reason'] ?? ''));

if ($paymentId <= 0) {
    die('Invalid payment.');
}
if ($reason === '') {
    $reason = 'Voided by user';
}

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['ss_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,
        ]);
    }
}

if (!function_exists('recalculate_folio_totals')) {
    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,
        ]);
    }
}

if (!function_exists('sync_order_payment_status')) {
    function sync_order_payment_status(PDO $pdo, int $orderId): void
    {
        $stmt = $pdo->prepare("
            SELECT
                COALESCE(SUM(total_charges), 0) AS total_charges,
                COALESCE(SUM(total_payments), 0) AS total_payments,
                COALESCE(SUM(balance), 0) AS total_balance
            FROM folios
            WHERE order_id = :order_id
        ");
        $stmt->execute([':order_id' => $orderId]);
        $folio = $stmt->fetch(PDO::FETCH_ASSOC);

        $payments = (float)($folio['total_payments'] ?? 0);
        $balance  = (float)($folio['total_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();

    /*
      Find the exact folio used by the payment from folio_transactions,
      not just any folio on the same booking.
    */
    $stmt = $pdo->prepare("
        SELECT
            p.*,
            ft.folio_id,
            ft.id AS original_folio_transaction_id,
            ft.tx_date AS original_tx_date,
            ft.description AS original_tx_description
        FROM payment p
        LEFT JOIN folio_transactions ft
            ON ft.source_table = 'payment'
           AND ft.source_id = p.id
           AND ft.tx_type = 'payment'
        WHERE p.id = :id
        LIMIT 1
    ");
    $stmt->execute([':id' => $paymentId]);
    $payment = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$payment) {
        throw new RuntimeException('Payment not found.');
    }

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

    $orderId = $orderId > 0 ? $orderId : (int)$payment['order_id'];
    $folioId = (int)($payment['folio_id'] ?? 0);
    $propertyId = (int)($payment['property_id'] ?? 0);

    if ($folioId <= 0) {
        throw new RuntimeException('Linked folio not found for this payment.');
    }

    $check = $pdo->prepare("
        SELECT COUNT(*)
        FROM folio_transactions
        WHERE source_table = 'payment_void'
          AND source_id = :source_id
          AND folio_id = :folio_id
    ");
    $check->execute([
        ':source_id' => $paymentId,
        ':folio_id'  => $folioId,
    ]);

    if ((int)$check->fetchColumn() > 0) {
        throw new RuntimeException('Payment already reversed in folio.');
    }

    $reversalDescription = 'Void payment #' . $paymentId . ' - ' . $reason;

    $stmt = $pdo->prepare("
        INSERT INTO folio_transactions
        (
            property_id,
            folio_id,
            order_id,
            tx_date,
            tx_type,
            description,
            debit,
            credit,
            source_table,
            source_id
        )
        VALUES
        (
            :property_id,
            :folio_id,
            :order_id,
            NOW(),
            'reversal',
            :description,
            :debit,
            :credit,
            'payment_void',
            :source_id
        )
    ");
    $stmt->execute([
        ':property_id' => $propertyId,
        ':folio_id'    => $folioId,
        ':order_id'    => $orderId,
        ':description' => $reversalDescription,
        ':debit'       => round((float)$payment['amount'], 2),
        ':credit'      => 0,
        ':source_id'   => $paymentId,
    ]);

    $reversalTxId = (int)$pdo->lastInsertId();

    $stmt = $pdo->prepare("
        UPDATE payment
        SET is_voided = 1,
            voided_at = NOW(),
            void_reason = :reason
        WHERE id = :id
    ");
    $stmt->execute([
        ':reason' => $reason,
        ':id'     => $paymentId,
    ]);

    write_payment_audit_log(
        $pdo,
        $propertyId,
        $orderId,
        $folioId,
        $paymentId,
        $reversalTxId,
        'void_payment',
        $reason,
        [
            'payment' => [
                'id'                         => (int)$payment['id'],
                'amount'                     => (float)$payment['amount'],
                'payment_method'             => (string)($payment['payment_method'] ?? ''),
                'date_time'                  => (string)($payment['date_time'] ?? ''),
                'reference'                  => (string)($payment['id_upload'] ?? ''),
                'currency'                   => (string)($payment['currency'] ?? ''),
                'original_folio_id'          => $folioId,
                'original_folio_tx_id'       => (int)($payment['original_folio_transaction_id'] ?? 0),
                'original_folio_description' => (string)($payment['original_tx_description'] ?? ''),
            ],
            'reversal_transaction' => [
                'id'          => $reversalTxId,
                'debit'       => round((float)$payment['amount'], 2),
                'credit'      => 0,
                'description' => $reversalDescription,
            ],
        ]
    );

    recalculate_folio_totals($pdo, $folioId);
    sync_order_payment_status($pdo, $orderId);

    $pdo->commit();
    header('Location: payments.php?order_id=' . $orderId . '&voided=1');
    exit;
} catch (Throwable $e) {
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    die($e->getMessage());
}