<?php
namespace App\Http\Controllers\Api\V1;

use App\Helpers\ApiResponse;
use App\Helpers\CacheHelper;
use App\Helpers\StripeHelper;
use App\Http\Controllers\Controller;
use App\Models\Franchisee;
use App\Models\Setting;
use App\Services\Stripe\PaymentGatewayConfigurationService;
use App\Services\Stripe\StripeConnectService;
use App\Services\Stripe\StripeWalletDomainService;
use App\Services\Stripe\StripeWebhookProcessor;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Symfony\Component\HttpFoundation\Response;

class ConnectPaymentController extends Controller
{
    public function __construct(
        private readonly PaymentGatewayConfigurationService $paymentGatewayConfiguration,
        private readonly StripeWalletDomainService $walletDomainService,
    ) {
    }

    /**
     * Connect a franchisee with a payment method account (Stripe, Viva).
     *
     * This method validates the input request, creates a connected account (e.g., Stripe),
     * generates an onboarding URL, checks for duplicates, and stores the account details.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function connectAccount(Request $request)
    {

        // Validate incoming request data
        $supportedCountries = [
            'us',
            'ca',
            'gb',
            'au',
            'nz',
            'sg',
            'hk',
            'jp',
            'ae',
            'il',
            'za',
            'fr',
            'de',
            'es',
            'it',
            'nl',
            'be',
            'at',
            'ch',
            'se',
            'no',
            'fi',
            'dk',
            'ie',
            'pt',
            'pl',
            'cz',
            'ro',
            'hu',
            'sk',
            'si',
            'lt',
            'lv',
            'ee',
            'lu',
            'mt',
            'cy',
            'gr',
            'mx',
            'br',
        ];

        $validator = Validator::make(
            $request->all(),
            [
                "payment_method_name" => 'required|in:stripe,viva',

                'country' => [
                    'required',
                    'string',
                    'size:2', // ISO country code like US, IN
                    Rule::in($supportedCountries)
                ],
                'email' => ["required", "string", "email", "max:255", "email:rfc,dns"],
                "status" => 'required|in:active,inactive',
                "franchisee_id" => 'required|uuid',
                'replace_existing' => 'sometimes|boolean',
                'force_new_account' => 'sometimes|boolean',
            ],
            [
                'country.in' => 'Stripe payments are not available for the selected country.'
            ]
        );
        if ($validator->fails()) {
            $firstError = ucfirst($validator->errors()->first());
            return ApiResponse::error(
                $firstError,
                Response::HTTP_UNPROCESSABLE_ENTITY
            );
        }
        try {

            $email = $request->email;
            $country = strtolower((string) $request->country);
            $message = '';
            $fid = $request->franchisee_id;
            $gateway = $request->payment_method_name;
            $replaceExisting = $request->boolean('replace_existing');
            $forceNewAccount = $request->boolean('force_new_account');
            $paymentGateway = [];
            switch ($request->payment_method_name) {
                case 'stripe':
                    $setting = Setting::where('franchisee_id', $fid)->first();
                    $existingStripe = data_get($setting?->payment_gateway_configuration, 'stripe');
                    $previousAccountId = data_get($existingStripe, 'stripe_account_id');

                    $reused = $this->paymentGatewayConfiguration->findReusableAccount(
                        $setting,
                        $email,
                        $country,
                        $forceNewAccount,
                    );

                    $accountCreated = false;

                    if ($reused !== null) {
                        $account = $reused['account'];
                        $onboardingUrl = StripeHelper::createAccountLink($account->id);
                    } else {
                        $account = StripeHelper::createConnectedAccount($email, $country);
                        $onboardingUrl = StripeHelper::createAccountLink($account->id);
                        $accountCreated = true;
                    }

                    // =============================================
                    // 4️⃣ Create setting if it does not exist
                    // =============================================

                    $setting = $setting ?? Setting::create([
                        'franchisee_id' => $fid,
                        'payment_gateway_configuration' => [],
                    ]);

                    // =============================================
                    // 5️⃣ Prepare gateway payload
                    // =============================================

                    $stripePayload = array_merge(
                        is_array($existingStripe) ? $existingStripe : [],
                        [
                            'franchise_id' => $request->fid,
                            'country' => $country,
                            'email' => $email,
                            'stripe_account_id' => $account->id,
                            'setup_url' => $onboardingUrl,
                            'status' => $request->status,
                            'response' => $account,
                            'previous_stripe_account_id' => ($accountCreated && $forceNewAccount && $previousAccountId && $previousAccountId !== $account->id)
                                ? $previousAccountId
                                : (($accountCreated && $previousAccountId && $previousAccountId !== $account->id)
                                    ? $previousAccountId
                                    : data_get($existingStripe, 'previous_stripe_account_id')),
                            'replaced_at' => ($accountCreated && $previousAccountId && $previousAccountId !== $account->id)
                                ? now()->toIso8601String()
                                : data_get($existingStripe, 'replaced_at'),
                            'reused_existing_account' => !$accountCreated,
                            'last_connected_at' => now()->toIso8601String(),
                        ],
                    );

                    $paymentGateway = [
                        $gateway => $stripePayload,
                    ];

                    // =============================================
                    // 6️⃣ Merge safely with existing JSON config
                    // =============================================

                    $existingConfig = (array) ($setting->payment_gateway_configuration ?? []);

                    $mergedConfig = array_merge($existingConfig, $paymentGateway);

                    $setting->payment_gateway_configuration = $this->paymentGatewayConfiguration->normalize($mergedConfig);

                    // =============================================
                    // 7️⃣ Save
                    // =============================================

                    $setting->save();
                    CacheHelper::forget('setting');

                    $message = 'Please note that the link will expire in 5 minutes.';
                    if (!$accountCreated) {
                        $message = 'Your existing Stripe connected account was re-linked. Complete onboarding using the link below (expires in 5 minutes).';
                    } elseif ($replaceExisting && $previousAccountId) {
                        $message = 'A new Stripe connected account was created. Complete onboarding using the link below (expires in 5 minutes). Your previous account ('.$previousAccountId.') is no longer linked to this franchise.';
                    }

                    break;

                case 'viva':
                /*
                $existing = FranchiseePaymentMethodDetail::where('franchise_id', $request->franchise_id)
                    ->where('payment_method_name', $request->payment_method_name)
                    ->first();

                if ($existing) {
                    return ApiResponse::error(
                        'This payment method is already set up for the selected franchisee.',
                        Response::HTTP_NOT_FOUND
                    );
                }
                // 1️ Create a new viva connected account
                $account = VivaHelper::createConnectedAccount($email, $country);
                // 2 Save the connected account details in the database
                $payment_method = new FranchiseePaymentMethodDetail([
                    'franchise_id' => $request->franchise_id,
                    'payment_method_name' => $request->payment_method_name,
                    'country' => $country,
                    'email' => $email,
                    'viva_id' => $account['accountId'],
                    'setup_url' => $account['invitation']['redirectUrl'],
                    'status' => $request->status,
                    'response' => json_encode($account, true)
                ]);
                $payment_method->save();
                break;*/

                default:
                    return ApiResponse::error(
                        'Invalid payment method.',
                        Response::HTTP_NOT_FOUND
                    );
            }
            return ApiResponse::success($paymentGateway, 'Your payment account was created and connected successfully. To complete the setup, please use the link provided below. ' . $message);
        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * Retrieve all active payment methods for a given franchisee.
     *
     * This method validates the input and returns a list of active payment methods
     * (e.g., Stripe, Viva) for the specified franchisee.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getPaymentAccounts(Request $request)
    {

        //Validate the incoming request parameters
        $validator = Validator::make($request->all(), [
            'franchisee_id' => 'required|integer|min:1',
        ]);
        if ($validator->fails()) {
            $firstError = ucfirst($validator->errors()->first());
            return ApiResponse::error(
                $firstError,
                Response::HTTP_UNPROCESSABLE_ENTITY
            );
        }

        try {

            $franchiseId = $request->franchise_id;
            //Fetch active payment methods for the franchisee
            $methods = Setting::where('franchise_id', $franchiseId)
                ->select('payment_gateway_configuration')
                ->get();
            //Return 404 if records found does not exist
            if (!$methods) {
                return ApiResponse::error(
                    "No active payment method found for this franchisee.",
                    Response::HTTP_NOT_FOUND
                );
            }
            // Return success with list of active methods
            return ApiResponse::success($methods, 'Active Payment method List ');

        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * Regenerate a Stripe connected account onboarding link.
     *
     * This endpoint is used to recreate the onboarding URL for a Stripe connected account.
     * The generated link is short-lived (expires in 5 minutes) and must be used promptly.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function regenerateAccountLink(Request $request)
    {

        //Validate incoming request parameters
        $Validator = Validator::make($request->all(), [
            'account_id' => 'required|string',
        ]);
        if ($Validator->fails()) {
            $firstError = ucfirst($Validator->errors()->first());
            return ApiResponse::error(
                $firstError,
                Response::HTTP_UNPROCESSABLE_ENTITY
            );
        }

        try {
            // Generate a new Stripe onboarding link for the connected account
            $onboardingUrl = StripeHelper::createAccountLink($request->account_id);

            return ApiResponse::success(['url' => $onboardingUrl], 'Account Setup Link. Please note that the link will expire in 5 minutes.');
        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * Refresh connected account details from Stripe after onboarding (admin session).
     */
    public function refreshStripeConnectStatus(Request $request)
    {
        $auth = Auth::user();

        if (!$auth || empty($auth->franchisee_id)) {
            return ApiResponse::error('Unauthorized', Response::HTTP_UNAUTHORIZED);
        }

        try {
            $setting = Setting::where('franchisee_id', $auth->franchisee_id)->first();

            if (!$setting) {
                return ApiResponse::error('Settings not found.', Response::HTTP_NOT_FOUND);
            }

            $existingStripe = data_get($setting->payment_gateway_configuration, 'stripe');
            $accountId = trim((string) data_get($existingStripe, 'stripe_account_id', ''));

            if ($accountId === '') {
                return ApiResponse::error(
                    'No Stripe connected account found. Connect Stripe first.',
                    Response::HTTP_NOT_FOUND
                );
            }

            $account = StripeHelper::retrieveConnectedAccount($accountId);
            $chargesEnabled = !empty($account->charges_enabled);

            $stripePayload = array_merge(
                is_array($existingStripe) ? $existingStripe : [],
                [
                    'stripe_account_id' => $account->id,
                    'email' => strtolower(trim((string) ($account->email ?? data_get($existingStripe, 'email', '')))),
                    'country' => strtolower((string) ($account->country ?? data_get($existingStripe, 'country', ''))),
                    'response' => $account,
                    'status' => data_get($existingStripe, 'status', 'active'),
                    'onboarding_completed_at' => $chargesEnabled
                        ? now()->toIso8601String()
                        : data_get($existingStripe, 'onboarding_completed_at'),
                ],
            );

            $walletMessage = null;
            if ($chargesEnabled) {
                $franchisee = Franchisee::find($auth->franchisee_id);
                if ($franchisee) {
                    $walletDomain = $this->walletDomainService->registerForFranchise($franchisee, $accountId);
                    $stripePayload['wallet_domain'] = $walletDomain;
                    $walletMessage = $this->walletDomainUserMessage($walletDomain);
                }
            }

            $mergedConfig = array_merge(
                (array) ($setting->payment_gateway_configuration ?? []),
                ['stripe' => $stripePayload],
            );

            $setting->payment_gateway_configuration = $this->paymentGatewayConfiguration->normalize($mergedConfig);
            $setting->save();
            CacheHelper::forget('setting');

            $baseMessage = $chargesEnabled
                ? 'Stripe onboarding complete. Your account is ready to accept payments.'
                : 'Stripe account refreshed. Complete any remaining requirements in Stripe if needed.';

            return ApiResponse::success(
                ['stripe' => $stripePayload],
                $walletMessage ? "{$baseMessage} {$walletMessage}" : $baseMessage
            );
        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * Create a one-time Stripe Dashboard login link for the franchise connected account.
     */
    public function stripeDashboardLink(Request $request)
    {
        $auth = Auth::user();

        if (!$auth || empty($auth->franchisee_id)) {
            return ApiResponse::error('Unauthorized', Response::HTTP_UNAUTHORIZED);
        }

        try {
            $setting = Setting::where('franchisee_id', $auth->franchisee_id)->first();

            if (!$setting) {
                return ApiResponse::error('Settings not found.', Response::HTTP_NOT_FOUND);
            }

            $accountId = trim((string) data_get($setting->payment_gateway_configuration, 'stripe.stripe_account_id', ''));

            if ($accountId === '') {
                return ApiResponse::error(
                    'No Stripe connected account found. Connect Stripe in Account Settings → Payment first.',
                    Response::HTTP_NOT_FOUND
                );
            }

            $access = app(StripeConnectService::class)->resolveDashboardAccess($accountId);

            $message = $access['requires_stripe_login']
                ? 'Opening your Stripe Dashboard. Sign in with the email you used when connecting Stripe if prompted.'
                : 'Stripe Dashboard link created. It expires shortly — open it in a new tab now.';

            return ApiResponse::success(
                [
                    'url' => $access['url'],
                    'account_type' => $access['account_type'],
                    'requires_stripe_login' => $access['requires_stripe_login'],
                ],
                $message
            );
        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * Register franchise domain for Apple Pay / Google Pay on the connected Stripe account.
     */
    public function registerWalletDomain(Request $request)
    {
        $auth = Auth::user();

        if (!$auth || empty($auth->franchisee_id)) {
            return ApiResponse::error('Unauthorized', Response::HTTP_UNAUTHORIZED);
        }

        try {
            $setting = Setting::where('franchisee_id', $auth->franchisee_id)->first();

            if (!$setting) {
                return ApiResponse::error('Settings not found.', Response::HTTP_NOT_FOUND);
            }

            $existingStripe = data_get($setting->payment_gateway_configuration, 'stripe');
            $accountId = trim((string) data_get($existingStripe, 'stripe_account_id', ''));

            if ($accountId === '') {
                return ApiResponse::error(
                    'No Stripe connected account found. Connect Stripe first.',
                    Response::HTTP_NOT_FOUND
                );
            }

            $franchisee = Franchisee::find($auth->franchisee_id);

            if (!$franchisee) {
                return ApiResponse::error('Franchisee not found.', Response::HTTP_NOT_FOUND);
            }

            $walletDomain = $this->walletDomainService->registerForFranchise($franchisee, $accountId);

            $stripePayload = array_merge(
                is_array($existingStripe) ? $existingStripe : [],
                ['wallet_domain' => $walletDomain],
            );

            $mergedConfig = array_merge(
                (array) ($setting->payment_gateway_configuration ?? []),
                ['stripe' => $stripePayload],
            );

            $setting->payment_gateway_configuration = $this->paymentGatewayConfiguration->normalize($mergedConfig);
            $setting->save();
            CacheHelper::forget('setting');

            return ApiResponse::success(
                ['stripe' => $stripePayload, 'wallet_domain' => $walletDomain],
                $this->walletDomainUserMessage($walletDomain) ?: 'Wallet domain registration attempted.'
            );
        } catch (\Throwable $e) {
            return ApiResponse::exception($e);
        }
    }

    /**
     * @param  array<string, mixed>  $walletDomain
     */
    private function walletDomainUserMessage(array $walletDomain): string
    {
        if (!empty($walletDomain['skipped'])) {
            return (string) ($walletDomain['skip_reason'] ?? '');
        }

        if (!empty($walletDomain['last_error'])) {
            return 'Wallet domain registration failed: '.$walletDomain['last_error'];
        }

        $domain = (string) ($walletDomain['domain_name'] ?? '');
        $apple = (string) ($walletDomain['apple_pay_status'] ?? 'unknown');
        $google = (string) ($walletDomain['google_pay_status'] ?? 'unknown');

        return "Wallet domain {$domain} registered (Apple Pay: {$apple}, Google Pay: {$google}).";
    }

    /**
     * Handle incoming Stripe webhook events for payment confirmations.
     *
     * This method verifies the webhook signature, processes the event type,
     * and updates local payment records accordingly (success or failure).
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function handleWebhookStripe(Request $request, StripeWebhookProcessor $processor)
    {
        $payload = $request->getContent();
        $sigHeader = $request->header('Stripe-Signature');

        try {
            $event = StripeHelper::stripeWebhook($payload, $sigHeader);
            $result = $processor->process($event);

            return response()->json([
                'status' => true,
                'message' => 'Webhook handled successfully',
                'data' => $result,
            ]);
        } catch (\UnexpectedValueException|\Stripe\Exception\SignatureVerificationException $e) {
            Log::warning('Stripe webhook signature invalid', ['message' => $e->getMessage()]);

            return response()->json(['error' => 'Invalid signature'], 400);
        } catch (\Throwable $e) {
            Log::error('Stripe webhook failed', [
                'message' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
            ]);

            return response()->json(['error' => 'Webhook processing failed'], 500);
        }
    }
}
