<?php

// PayHere card payment integration for the customer ordering app.
// Card details are entered on PayHere's own hosted popup — they never touch our server,
// so this stays PCI-compliant on our side. We only ever see a signed success/failure notification.
class PayHereController extends Controller
{
    private function jsonInput(): array
    {
        $raw = file_get_contents('php://input');
        $data = json_decode($raw, true);
        return is_array($data) ? $data : [];
    }

    // Called by the customer app right after an order is placed with payment_method = CARD.
    // Returns everything payhere.js needs to open the secure payment popup, including a
    // tamper-proof hash so PayHere can verify the amount wasn't changed client-side.
    public function initiate(): void
    {
        $body = $this->jsonInput();
        $orderId = (int) ($body['order_id'] ?? 0);

        $merchantId = setting('payhere_merchant_id', '');
        $merchantSecret = setting('payhere_merchant_secret', '');
        $mode = setting('payhere_mode', 'sandbox');

        if (!$merchantId || !$merchantSecret) {
            $this->json(['success' => false, 'message' => 'Card payment isn\'t configured yet. Please choose Bank Transfer or Cash on Delivery instead.'], 422);
            return;
        }

        $orderModel = new Order();
        $order = $orderModel->find($orderId);
        if (!$order) {
            $this->json(['success' => false, 'message' => 'Order not found.'], 404);
            return;
        }

        $db = Database::getConnection();
        $customer = null;
        if ($order['customer_id']) {
            $stmt = $db->prepare("SELECT * FROM customers WHERE id = :id");
            $stmt->execute(['id' => $order['customer_id']]);
            $customer = $stmt->fetch();
        }

        $amount = number_format((float) $order['total_amount'], 2, '.', '');
        $currency = 'LKR';

        // PayHere hash spec: MD5(merchant_id + order_id + amount + currency + MD5(merchant_secret)) uppercased
        $hash = strtoupper(md5(
            $merchantId . $orderId . $amount . $currency . strtoupper(md5($merchantSecret))
        ));

        $baseUrl = setting('public_base_url') ?: BASE_URL;
        $nameParts = explode(' ', trim($customer['full_name'] ?? 'Customer'), 2);

        $this->json([
            'success' => true,
            'sandbox' => $mode !== 'live',
            'payment' => [
                'sandbox'     => $mode !== 'live',
                'merchant_id' => $merchantId,
                'return_url'  => $baseUrl . '/track?order=' . urlencode($order['order_number']) . '&payhere=success',
                'cancel_url'  => $baseUrl . '/track?order=' . urlencode($order['order_number']) . '&payhere=cancelled',
                'notify_url'  => $baseUrl . '/api/payhere/notify',
                'order_id'    => (string) $orderId,
                'items'       => 'Order ' . $order['order_number'],
                'amount'      => $amount,
                'currency'    => $currency,
                'first_name'  => $nameParts[0] ?? 'Customer',
                'last_name'   => $nameParts[1] ?? '',
                'email'       => $customer['email'] ?? 'customer@example.com',
                'phone'       => $customer['phone'] ?? '',
                'address'     => $order['delivery_address'] ?? '',
                'city'        => 'Anuradhapura',
                'country'     => 'Sri Lanka',
                'hash'        => $hash,
            ],
        ]);
    }

    // Server-to-server notification PayHere calls once the payment finishes.
    // Must verify the signature before trusting anything in here.
    public function notify(): void
    {
        $merchantId     = setting('payhere_merchant_id', '');
        $merchantSecret = setting('payhere_merchant_secret', '');

        $postedMerchantId = $_POST['merchant_id'] ?? '';
        $orderId          = (int) ($_POST['order_id'] ?? 0);
        $amount           = $_POST['payhere_amount'] ?? '0';
        $currency         = $_POST['payhere_currency'] ?? 'LKR';
        $statusCode       = $_POST['status_code'] ?? '';
        $md5sig           = $_POST['md5sig'] ?? '';

        $expectedSig = strtoupper(md5(
            $postedMerchantId . $orderId . $amount . $currency . $statusCode . strtoupper(md5($merchantSecret))
        ));

        if (!$merchantSecret || $postedMerchantId !== $merchantId || $md5sig !== $expectedSig) {
            AuditLogger::log(null, 'PAYHERE_IPN_INVALID', 'payments', "Rejected IPN for order #{$orderId} — signature mismatch");
            http_response_code(400);
            exit;
        }

        // status_code: 2 = success, 0 = pending, -1 = cancelled, -2 = failed, -3 = chargedback
        if ($statusCode === '2') {
            $orderModel = new Order();
            $order = $orderModel->find($orderId);
            if ($order) {
                $db = Database::getConnection();
                $methodStmt = $db->prepare("SELECT id FROM payment_methods WHERE method_name = 'PAYHERE' LIMIT 1");
                $methodStmt->execute();
                $method = $methodStmt->fetch();
                $methodId = $method ? (int) $method['id'] : null;

                if (!$methodId) {
                    $db->prepare(
                        "INSERT INTO payment_methods (method_name, finance_account_id, fee_percentage, is_active) VALUES ('PAYHERE', NULL, 0, 1)"
                    )->execute();
                    $methodId = (int) $db->lastInsertId();
                }

                // Avoid double-recording if PayHere retries the notification
                $existingStmt = $db->prepare("SELECT COUNT(*) as cnt FROM payments WHERE order_id = :id AND status = 'PAID'");
                $existingStmt->execute(['id' => $orderId]);
                if ((int) $existingStmt->fetch()['cnt'] === 0) {
                    $orderModel->recordPayment($orderId, $methodId, (float) $amount, null);
                    AuditLogger::log(null, 'PAYHERE_PAYMENT_SUCCESS', 'payments', "PayHere payment confirmed for order #{$orderId}, amount " . formatMoney((float) $amount));
                }
            }
        } else {
            AuditLogger::log(null, 'PAYHERE_PAYMENT_NOT_SUCCESSFUL', 'payments', "PayHere status_code={$statusCode} for order #{$orderId}");
        }

        http_response_code(200);
        exit;
    }
}
