<?php

namespace App\Services;

use App\Models\Customer;
use App\Models\PaymentOrder;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use App\Services\Contracts\AikDigitalClientInterface;


/**
 * Creates and manages payment orders via the AIK Digital Payment API (Section 5).
 */
class PaymentService
{
   public function __construct(private AikDigitalClientInterface $apiClient) {}

    /**
     * Create a payment order with AIK Digital.
     *
     * @param Customer $customer
     * @param string   $policyTitle  Human-readable policy name
     * @param int      $amountPkr    Amount in PKR (whole rupees)
     * @param string|null $productCode
     * @return PaymentOrder
     */
    public function createPaymentOrder(
        Customer $customer,
        string $policyTitle,
        int $amountPkr,
        ?string $productCode = null
    ): PaymentOrder {

        $orderId = $this->generateOrderId();

        $payload = [
            'merchantID'        => config('aikdigital.merchant_id'),
            'paymentOrderTitle' => $policyTitle,
            'paymentOrderID'    => $orderId,
            'paymentAmount'     => (string) $amountPkr,
            'paymentMode'       => 'CASHIER',
            'openId'            => $customer->open_id,
            'msisdn'            => $customer->msisdn,
            'paymentNotifyUrl'  => config('aikdigital.payment_notify_url'),
            'paymentExpiryTime' => now()->addHour()->toIso8601String(),
        ];

        if ($productCode) {
            $payload['productCode'] = $productCode;
        }

        // Persist order BEFORE calling API (idempotent guard)
        $order = PaymentOrder::create([
            'customer_id'      => $customer->id,
            'payment_order_id' => $orderId,
            'policy_title'     => $policyTitle,
            'amount'           => $amountPkr,
            'status'           => PaymentOrder::STATUS_PENDING,
            'product_code'     => $productCode,
        ]);

        Log::info("PaymentService: calling createPayment orderId={$orderId}");

        $response = $this->apiClient->createPayment($payload);

        $resultStatus = $response['result']['resultStatus'] ?? 'U';
        $resultCode   = $response['result']['resultCode']   ?? '';

        $order->update([
            'aik_transaction_id' => $response['paymentTransactionID'] ?? null,
            'aik_order_status'   => $response['orderStatus']          ?? null,
            'aik_result_status'  => $resultStatus,
            'aik_result_code'    => $resultCode,
            'aik_raw_response'   => json_encode($response),
        ]);

        Log::info("PaymentService: createPayment response status={$resultStatus} orderId={$orderId}");

        return $order->fresh();
    }

    /**
     * Process an inbound payment callback from AIK Digital (Section 6).
     *
     * Steps:
     *  1. Find the order
     *  2. Idempotency check
     *  3. Validate amount + customer
     *  4. Apply status (S → activate, F → fail, U → escalate)
     *
     * @throws \InvalidArgumentException on validation failure
     */
    public function processCallback(array $payload): PaymentOrder
    {
        $txnId  = $payload['paymentTransactionID'] ?? null;
        $status = $payload['resultStatus']         ?? $payload['orderStatus'] ?? 'U';
        $amount = $payload['paymentAmount']        ?? null;
        $openId = $payload['openId']               ?? null;
        $msisdn = $payload['msisdn']               ?? null;

        // Look up order by AIK transaction ID or by EFU order ID
        $order = PaymentOrder::where('aik_transaction_id', $txnId)
            ->orWhere('payment_order_id', $payload['paymentOrderID'] ?? null)
            ->first();

        if (!$order) {
            Log::error("PaymentService: callback for unknown transaction txnId={$txnId}");
            throw new \InvalidArgumentException("Order not found for transaction: {$txnId}");
        }

        // Idempotency — already processed
        if (in_array($order->status, [PaymentOrder::STATUS_SUCCESS, PaymentOrder::STATUS_FAILED])) {
            Log::info("PaymentService: duplicate callback ignored for orderId={$order->payment_order_id}");
            return $order;
        }

        // Amount validation
        if ($amount !== null && (int)$amount !== (int)$order->amount) {
            Log::error("PaymentService: amount mismatch! callback={$amount} order={$order->amount}");
            $order->update(['status' => PaymentOrder::STATUS_MISMATCH]);
            throw new \InvalidArgumentException('Payment amount mismatch — flagged for review.');
        }

        // Customer validation
        $customer = $order->customer;
        if ($openId && $customer->open_id !== $openId) {
            Log::error("PaymentService: openId mismatch for orderId={$order->payment_order_id}");
            throw new \InvalidArgumentException('Customer openId mismatch.');
        }

        // Apply status
        $newStatus = match ($status) {
            'S'     => PaymentOrder::STATUS_SUCCESS,
            'F'     => PaymentOrder::STATUS_FAILED,
            default => PaymentOrder::STATUS_UNKNOWN,
        };

        $order->update([
            'status'             => $newStatus,
            'aik_result_status'  => $status,
            'aik_result_code'    => $payload['resultCode'] ?? null,
            'callback_received_at' => now(),
            'aik_raw_callback'   => json_encode($payload),
        ]);

        if ($newStatus === PaymentOrder::STATUS_SUCCESS) {
            // TODO: trigger policy activation logic here
            Log::info("PaymentService: POLICY ACTIVATED for orderId={$order->payment_order_id}");
            $order->update(['policy_activated_at' => now()]);
        } elseif ($newStatus === PaymentOrder::STATUS_FAILED) {
            Log::warning("PaymentService: payment FAILED orderId={$order->payment_order_id} code=" . ($payload['resultCode'] ?? ''));
        } else {
            Log::error("PaymentService: payment UNKNOWN orderId={$order->payment_order_id} — escalate to AIK Digital txnId={$txnId}");
        }

        return $order->fresh();
    }

    // ─────────────────────────────────────────────────────────────────────────

    private function generateOrderId(): string
    {
        return 'EFU-ORD-' . now()->format('Ymd') . '-' . strtoupper(Str::random(8));
    }
}
