<?php

namespace App\Services\Stripe;

use App\Helpers\StripeHelper;
use App\Models\FormSubmission;
use App\Models\Payment;
use App\Models\PaymentInvoice;
use App\Services\PaymentNotificationService;
use App\Services\PaymentInvoiceService;
use Illuminate\Support\Facades\Log;

class PaymentStatusSyncService
{
    public function __construct(
        protected PaymentIntentService $paymentIntentService,
        protected PaymentInvoiceService $invoiceService,
        protected StripeConnectService $connectService,
        protected PaymentNotificationService $paymentNotificationService,
    ) {
    }

    /**
     * Reconcile local payment/invoice rows with Stripe (fallback when webhook is delayed).
     *
     * @return array<string, mixed>
     */
    public function syncByRegistrationId(string $registrationId, ?string $paymentIntentId = null): array
    {
        if (!$paymentIntentId) {
            $paymentIntentId = $this->discoverIntentIdFromStripe($registrationId);
        }

        $payment = $this->resolveOrCreatePayment($registrationId, $paymentIntentId);

        if (!$payment || empty($payment->payment_intent_id)) {
            return [
                'payment_status' => null,
                'invoice_status' => null,
                'stripe_status' => null,
                'synced' => false,
                'is_paid' => false,
            ];
        }

        $intentId = $payment->payment_intent_id;
        $stripeAccountCandidates = $this->connectService->collectStripeAccountCandidatesForPayment($payment);
        $stripeStatus = null;
        Log::info('PaymentStatusSyncService: resolving Stripe intent', [
            'registration_id' => $registrationId,
            'intent_id' => $intentId,
            'stripe_account_candidates' => $stripeAccountCandidates,
        ]);

        $retrieveError = null;

        foreach ($stripeAccountCandidates as $stripeAccount) {
            try {
                if (str_starts_with($intentId, 'seti_')) {
                    $intent = $this->paymentIntentService->retrieveSetupIntent($intentId, $stripeAccount);
                    $stripeStatus = $intent->status ?? null;
                    if ($stripeStatus === 'succeeded') {
                        $this->markSucceeded($payment, $intentId);
                    }
                } else {
                    $intent = $this->paymentIntentService->retrievePaymentIntent($intentId, $stripeAccount);
                    $stripeStatus = $intent->status ?? null;
                    if ($stripeStatus === 'succeeded') {
                        $this->markSucceeded($payment, $intentId, $intent->toArray());
                    } elseif ($stripeStatus === 'processing') {
                        $payment->update(['status' => 'processing']);
                    } elseif (in_array($stripeStatus, ['requires_payment_method', 'canceled'], true)) {
                        $payment->update(['status' => 'failed']);
                        PaymentInvoice::where('payment_intent_id', $intentId)->update([
                            'status' => PaymentInvoice::STATUS_FAILED,
                        ]);
                    }
                }

                $retrieveError = null;
                break;
            } catch (\Throwable $e) {
                $retrieveError = $e;
                Log::info('PaymentStatusSyncService: Stripe retrieve attempt failed', [
                    'registration_id' => $registrationId,
                    'intent_id' => $intentId,
                    'stripe_account_id' => $stripeAccount,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        if ($retrieveError !== null) {
            Log::warning('PaymentStatusSyncService: Stripe retrieve failed for all candidates', [
                'registration_id' => $registrationId,
                'intent_id' => $intentId,
                'stripe_account_candidates' => $stripeAccountCandidates,
                'error' => $retrieveError->getMessage(),
            ]);
        }

        $payment->refresh();
        $invoice = PaymentInvoice::where('payment_intent_id', $intentId)->first();

        return [
            'payment_status' => $payment->status,
            'invoice_status' => $invoice?->status,
            'stripe_status' => $stripeStatus,
            'synced' => true,
            'payment_intent_id' => $intentId,
            'is_paid' => $payment->status === 'succeeded' || $invoice?->status === PaymentInvoice::STATUS_PAID,
        ];
    }

    /**
     * Find, relink, or create a payment row for a Stripe intent + registration.
     */
    public function resolveOrCreatePayment(string $registrationId, ?string $paymentIntentId = null): ?Payment
    {
        if ($paymentIntentId) {
            $byIntent = Payment::where('payment_intent_id', $paymentIntentId)->first();
            if ($byIntent) {
                return $byIntent;
            }
        }

        $byRegistration = Payment::where('registration_id', $registrationId)->latest()->first();
        if ($byRegistration) {
            if ($paymentIntentId && $byRegistration->payment_intent_id !== $paymentIntentId) {
                $byRegistration->update(['payment_intent_id' => $paymentIntentId]);
            }

            return $byRegistration->fresh();
        }

        if ($paymentIntentId) {
            return $this->createPaymentFromStripeIntent($registrationId, $paymentIntentId);
        }

        return null;
    }

    public function createPaymentFromStripeIntent(string $registrationId, string $intentId): ?Payment
    {
        try {
            StripeHelper::init();

            $submission = FormSubmission::find($registrationId);
            if (!$submission) {
                Log::warning('PaymentStatusSyncService: submission not found for reconcile', [
                    'registration_id' => $registrationId,
                    'intent_id' => $intentId,
                ]);

                return null;
            }

            $userEmail = null;
            $amount = 0;
            $currency = 'usd';
            $stripeStatus = 'pending';
            $response = [];
            $stripeAccount = $this->connectService->resolveStripeAccountForFranchisee($submission->franchisee_id);

            if (str_starts_with($intentId, 'seti_')) {
                $intent = $this->paymentIntentService->retrieveSetupIntent($intentId, $stripeAccount);
                $stripeStatus = $intent->status ?? 'pending';
                $response = $intent->toArray();
                $userEmail = $intent->metadata->user_email ?? null;
                $stripeAccount = $stripeAccount ?? ($intent->metadata->stripe_account_id ?? null);
            } else {
                $intent = $this->paymentIntentService->retrievePaymentIntent($intentId, $stripeAccount);
                $stripeStatus = $intent->status ?? 'pending';
                $response = $intent->toArray();
                $amount = ($intent->amount ?? 0) / 100;
                $currency = $intent->currency ?? 'usd';
                $userEmail = $intent->metadata->user_email ?? null;
                $stripeAccount = $stripeAccount ?? ($intent->metadata->stripe_account_id ?? null);

                $metaRegId = $intent->metadata->registration_id ?? null;
                if ($metaRegId && $metaRegId !== $registrationId) {
                    Log::warning('PaymentStatusSyncService: registration_id mismatch in Stripe metadata', [
                        'expected' => $registrationId,
                        'metadata' => $metaRegId,
                        'intent_id' => $intentId,
                    ]);
                }
            }

            if (!$userEmail) {
                $userEmail = $this->extractEmailFromSubmission($submission);
            }

            $localStatus = $stripeStatus === 'succeeded' ? 'succeeded' : ($stripeStatus === 'processing' ? 'processing' : 'pending');

            $payment = Payment::create([
                'franchisee_id' => $submission->franchisee_id,
                'registration_id' => $registrationId,
                'user_email' => $userEmail ?? 'unknown@local.test',
                'payment_type' => 'stripe',
                'payment_intent_id' => $intentId,
                'amount' => $amount,
                'currency' => $currency,
                'status' => $localStatus,
                'response' => $response,
            ]);

            $existingInvoice = PaymentInvoice::where('payment_intent_id', $intentId)->first();
            if (!$existingInvoice) {
                $this->invoiceService->create([
                    'customer_email' => $userEmail,
                    'franchisee_id' => $submission->franchisee_id,
                    'registration_id' => $registrationId,
                    'payment_type' => 'stripe',
                    'payment_method' => 'card',
                    'payment_intent_id' => $intentId,
                    'subtotal' => $amount,
                    'tax' => 0,
                    'total' => $amount,
                    'currency' => $currency,
                    'status' => $localStatus === 'succeeded' ? PaymentInvoice::STATUS_PAID : PaymentInvoice::STATUS_DRAFT,
                    'paid_at' => $localStatus === 'succeeded' ? now() : null,
                    'details' => $submission->submission_data,
                    'response' => $response,
                ]);
            }

            Log::info('PaymentStatusSyncService: reconciled payment from Stripe', [
                'registration_id' => $registrationId,
                'intent_id' => $intentId,
                'payment_id' => $payment->id,
            ]);

            return $payment;
        } catch (\Throwable $e) {
            Log::error('PaymentStatusSyncService: failed to reconcile payment', [
                'registration_id' => $registrationId,
                'intent_id' => $intentId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    /**
     * Find the latest Stripe intent for this registration when local rows are missing.
     */
    public function discoverIntentIdFromStripe(string $registrationId): ?string
    {
        try {
            StripeHelper::init();
            $stripe = $this->connectService->client();

            $submission = FormSubmission::find($registrationId);
            $stripeAccount = $submission
                ? $this->connectService->resolveStripeAccountForFranchisee($submission->franchisee_id)
                : null;

            $searchOptions = $this->connectService->connectRequestOptions($stripeAccount);

            $result = $stripe->paymentIntents->search([
                'query' => "metadata['registration_id']:'{$registrationId}'",
                'limit' => 10,
            ], $searchOptions);

            $succeeded = null;
            $latest = null;

            foreach ($result->data ?? [] as $intent) {
                $latest = $latest ?? $intent->id;
                if (($intent->status ?? '') === 'succeeded') {
                    $succeeded = $intent->id;
                    break;
                }
            }

            return $succeeded ?? $latest;
        } catch (\Throwable $e) {
            Log::debug('PaymentStatusSyncService: Stripe intent search skipped', [
                'registration_id' => $registrationId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    private function extractEmailFromSubmission(FormSubmission $submission): ?string
    {
        $data = $submission->submission_data ?? [];
        if (!is_array($data)) {
            return null;
        }

        foreach ($data as $item) {
            if (!is_array($item)) {
                continue;
            }
            if (!empty($item['email'])) {
                return $item['email'];
            }
            if (!empty($item['parent_email'])) {
                return $item['parent_email'];
            }
        }

        return null;
    }

    private function markSucceeded(Payment $payment, string $intentId, ?array $response = null): void
    {
        if ($payment->status !== 'succeeded') {
            $payment->update([
                'status' => 'succeeded',
                'response' => $response ?? $payment->response,
            ]);
        } elseif ($response !== null) {
            $payment->update(['response' => $response]);
        }

        PaymentInvoice::where('payment_intent_id', $intentId)->update([
            'status' => PaymentInvoice::STATUS_PAID,
            'paid_at' => now(),
        ]);

        $payment->refresh();
        $invoice = PaymentInvoice::where('payment_intent_id', $intentId)->first();

        $this->dispatchPaymentSuccessNotification($payment, $invoice, $intentId);
    }

    private function dispatchPaymentSuccessNotification(
        Payment $payment,
        ?PaymentInvoice $invoice,
        string $intentId,
    ): void {
        try {
            $this->paymentNotificationService->notifyForPayment(
                $payment,
                'payment_success',
                null,
                $invoice
            );
        } catch (\Throwable $e) {
            Log::warning('PaymentStatusSyncService: payment_success notification failed', [
                'registration_id' => $payment->registration_id,
                'intent_id' => $intentId,
                'error' => $e->getMessage(),
            ]);
        }
    }

}
