<?php

namespace App\Services;

use App\Models\Company;
use App\Models\SubscriptionPayment;
use App\Models\SubscriptionPlan;
use App\Models\SubscriptionRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Razorpay\Api\Api;
use RuntimeException;

class RazorpaySubscriptionPaymentService
{
    public function __construct(
        protected PlanPricingService $pricingService,
        protected CouponService $couponService
    ) {}

    /**
     * @return array{0: ?string, 1: ?string}
     */
    public function getKeys(): array
    {
        $path = storage_path('app/superadmin_settings.json');
        if (File::exists($path)) {
            $data = json_decode(File::get($path), true);
            if (is_array($data)) {
                $keyId = $data['razorpay_key_id'] ?? null;
                $keySecret = $data['razorpay_key_secret'] ?? null;
                if ($keyId && $keySecret) {
                    return [$keyId, $keySecret];
                }
            }
        }

        return [config('razorpay.key_id'), config('razorpay.key_secret')];
    }

    public function api(): Api
    {
        [$keyId, $keySecret] = $this->getKeys();
        if (! $keyId || ! $keySecret) {
            throw new RuntimeException('Razorpay is not configured.');
        }

        return new Api($keyId, $keySecret);
    }

    /**
     * @param  array<string, mixed>  $quote
     * @return array<string, mixed>
     */
    public function createOrderForCompany(
        Company $company,
        SubscriptionPlan $plan,
        array $quote,
        ?int $recordedBy = null,
        bool $applyPlanOnSuccess = true,
        bool $extendLicenceOnSuccess = false,
        ?int $subscriptionRequestId = null
    ): array {
        $amount = (float) ($quote['total'] ?? 0);
        if ($amount < 0.01) {
            throw new RuntimeException('Payable amount must be at least ₹0.01.');
        }

        $api = $this->api();
        [$keyId] = $this->getKeys();
        $currency = config('razorpay.currency', 'INR');
        $amountPaise = (int) round($amount * 100);

        $order = $api->order->create([
            'amount' => $amountPaise,
            'currency' => $currency,
            'receipt' => 'sub_' . $company->id . '_' . time(),
        ]);

        $payment = SubscriptionPayment::create([
            'company_id' => $company->id,
            'subscription_plan_id' => $plan->id,
            'subscription_request_id' => $subscriptionRequestId,
            'coupon_id' => $quote['coupon']?->id ?? null,
            'checkout_mode' => $quote['mode'] ?? 'purchase',
            'is_upgrade' => (bool) ($quote['is_upgrade'] ?? false),
            'plan_discount_amount' => $quote['plan_discount_amount'] ?? 0,
            'coupon_discount_amount' => $quote['coupon_discount_amount'] ?? 0,
            'apply_plan_on_success' => $applyPlanOnSuccess,
            'extend_licence_on_success' => $extendLicenceOnSuccess || in_array($quote['mode'] ?? '', ['purchase', 'renew'], true),
            'employee_count' => $plan->employee_limit ?? 1,
            'amount' => $amount,
            'calculated_total' => $amount,
            'currency' => $currency,
            'payment_method' => 'razorpay',
            'status' => 'pending',
            'razorpay_order_id' => $order['id'],
            'recorded_by' => $recordedBy,
            'meta' => [
                'quote' => collect($quote)->except('coupon')->all(),
                'days_remaining' => $quote['days_remaining'] ?? null,
                'unused_credit' => $quote['unused_credit'] ?? null,
                'upgrade_gross' => $quote['upgrade_gross'] ?? null,
            ],
        ]);

        return [
            'order_id' => $order['id'],
            'amount' => $order['amount'],
            'currency' => $order['currency'],
            'company_id' => (int) $company->id,
            'payment_id' => $payment->id,
            'key' => $keyId,
            'payable' => $amount,
        ];
    }

    /**
     * @return array{ok: bool, payment?: SubscriptionPayment, message?: string}
     */
    public function verifyAndFulfill(
        string $razorpayOrderId,
        string $razorpayPaymentId,
        string $razorpaySignature
    ): array {
        try {
            $api = $this->api();
            $api->utility->verifyPaymentSignature([
                'razorpay_order_id' => $razorpayOrderId,
                'razorpay_payment_id' => $razorpayPaymentId,
                'razorpay_signature' => $razorpaySignature,
            ]);
        } catch (\Throwable $e) {
            SubscriptionPayment::where('razorpay_order_id', $razorpayOrderId)->update([
                'status' => 'failed',
                'razorpay_payment_id' => $razorpayPaymentId,
            ]);

            return ['ok' => false, 'message' => 'Payment verification failed.'];
        }

        return DB::transaction(function () use ($razorpayOrderId, $razorpayPaymentId, $razorpaySignature) {
            $payment = SubscriptionPayment::query()
                ->where('razorpay_order_id', $razorpayOrderId)
                ->lockForUpdate()
                ->first();

            if (! $payment) {
                return ['ok' => false, 'message' => 'Payment record not found.'];
            }

            if ($payment->status === 'completed') {
                return ['ok' => true, 'payment' => $payment];
            }

            $payment->update([
                'status' => 'completed',
                'razorpay_payment_id' => $razorpayPaymentId,
                'razorpay_signature' => $razorpaySignature,
                'paid_at' => now(),
            ]);

            $this->applyEntitlements($payment);

            return ['ok' => true, 'payment' => $payment->fresh()];
        });
    }

    /**
     * Handle Razorpay server webhook (payment.captured) with HMAC signature verification.
     *
     * @return array{ok: bool, message?: string, payment?: SubscriptionPayment}
     */
    public function handleWebhook(string $payload, ?string $signatureHeader): array
    {
        [, $keySecret] = $this->getKeys();
        if (! $keySecret) {
            return ['ok' => false, 'message' => 'Razorpay is not configured.'];
        }

        $expected = hash_hmac('sha256', $payload, $keySecret);
        if (! is_string($signatureHeader) || ! hash_equals($expected, $signatureHeader)) {
            return ['ok' => false, 'message' => 'Invalid webhook signature.'];
        }

        $data = json_decode($payload, true);
        if (! is_array($data)) {
            return ['ok' => false, 'message' => 'Invalid webhook payload.'];
        }

        $event = (string) ($data['event'] ?? '');
        if ($event !== 'payment.captured') {
            return ['ok' => true, 'message' => 'Event ignored.'];
        }

        $entity = $data['payload']['payment']['entity'] ?? null;
        if (! is_array($entity)) {
            return ['ok' => false, 'message' => 'Missing payment entity.'];
        }

        $orderId = (string) ($entity['order_id'] ?? '');
        $paymentId = (string) ($entity['id'] ?? '');
        if ($orderId === '' || $paymentId === '') {
            return ['ok' => false, 'message' => 'Missing order or payment id.'];
        }

        return DB::transaction(function () use ($orderId, $paymentId, $data) {
            $payment = SubscriptionPayment::query()
                ->where('razorpay_order_id', $orderId)
                ->lockForUpdate()
                ->first();

            if (! $payment) {
                return ['ok' => false, 'message' => 'Payment record not found.'];
            }

            if ($payment->status === 'completed') {
                return ['ok' => true, 'payment' => $payment];
            }

            $payment->update([
                'status' => 'completed',
                'razorpay_payment_id' => $paymentId,
                'paid_at' => now(),
                'meta' => array_merge((array) ($payment->meta ?? []), [
                    'webhook_event' => 'payment.captured',
                    'webhook_at' => now()->toIso8601String(),
                ]),
            ]);

            $this->applyEntitlements($payment);

            return ['ok' => true, 'payment' => $payment->fresh()];
        });
    }

    public function applyEntitlements(SubscriptionPayment $payment): void
    {
        $company = Company::withoutGlobalScopes()->find($payment->company_id);
        if (! $company) {
            return;
        }

        $updates = ['status' => 'active'];

        if ($payment->apply_plan_on_success && $payment->subscription_plan_id) {
            $updates['subscription_plan_id'] = $payment->subscription_plan_id;
        }

        $isUpgrade = (bool) $payment->is_upgrade || ($payment->checkout_mode === 'upgrade');
        if ($payment->extend_licence_on_success && ! $isUpgrade) {
            $updates['licence_expire_on'] = now()->addYear()->toDateString();
            $updates['package_type'] = 'yearly';
        }

        $company->forceFill($updates)->save();

        if ($payment->subscription_request_id) {
            SubscriptionRequest::where('id', $payment->subscription_request_id)
                ->where('status', 'pending')
                ->update([
                    'status' => 'approved',
                    'approved_at' => now(),
                    'approved_by' => $payment->recorded_by,
                ]);
        }

        if ($payment->coupon_id) {
            $coupon = $payment->coupon_id
                ? \App\Models\SubscriptionCoupon::find($payment->coupon_id)
                : null;
            if ($coupon) {
                $already = \App\Models\SubscriptionCouponRedemption::query()
                    ->where('subscription_coupon_id', $coupon->id)
                    ->where('subscription_payment_id', $payment->id)
                    ->exists();
                if (! $already) {
                    $this->couponService->redeem(
                        $coupon,
                        $company,
                        $payment->id,
                        $payment->subscription_request_id
                    );
                }
            }
        }
    }
}
