<?php

namespace App\Http\Controllers\Tenant;

use App\Http\Controllers\Controller;
use App\Models\GatewayPayment;
use App\Models\Invoice;
use App\Models\SubscriptionPlan;
use App\Models\SystemSetting;
use App\Models\TenantPayment;
use App\Services\NumberGenerator;
use App\Services\PaymentGatewayService;
use App\Services\SubscriptionService;
use App\Services\TenantLifecycleNotificationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;

class BillingController extends Controller
{
    public function index(): View
    {
        return view('tenant.billing.index', [
            'tenant' => auth()->user()->tenant,
            'plans' => SubscriptionPlan::where('is_active', true)->orderBy('price_monthly')->get(),
            'payments' => TenantPayment::with('plan')->latest()->paginate(10),
            'invoices' => Invoice::latest()->limit(10)->get(),
            'bankDetails' => SystemSetting::getValue('bank_details', 'Add bank transfer instructions in Super Admin settings.'),
            'gatewayEnabled' => app(PaymentGatewayService::class)->supportsHostedCheckout(),
        ]);
    }

    public function storePayment(Request $request, TenantLifecycleNotificationService $notifications): RedirectResponse
    {
        $validated = $request->validate([
            'subscription_plan_id' => ['required', 'exists:subscription_plans,id'],
            'billing_cycle' => ['required', 'in:monthly,yearly'],
            'proof' => ['required', 'file', 'mimes:jpg,jpeg,png,pdf', 'max:4096'],
            'notes' => ['nullable', 'string', 'max:1000'],
        ]);

        $plan = SubscriptionPlan::findOrFail($validated['subscription_plan_id']);
        $amount = $validated['billing_cycle'] === 'yearly' ? $plan->price_yearly : $plan->price_monthly;

        if ($request->hasFile('proof')) {
            $validated['proof_path'] = $request->file('proof')->store('payment-proofs', 'uploads');
        }

        $payment = TenantPayment::create([
            'tenant_id' => $request->user()->tenant_id,
            'subscription_plan_id' => $plan->id,
            'amount' => $amount,
            'billing_cycle' => $validated['billing_cycle'],
            'payment_channel' => 'manual',
            'is_auto_billed' => false,
            'status' => 'pending',
            'reference' => NumberGenerator::reference('SUB'),
            'proof_path' => $validated['proof_path'] ?? null,
            'notes' => $validated['notes'] ?? null,
        ]);

        $notifications->paymentSubmitted($payment);

        return back()->with('status', 'Payment submitted for approval.');
    }

    public function startGatewayPayment(Request $request, PaymentGatewayService $gateway): RedirectResponse
    {
        $validated = $request->validate([
            'subscription_plan_id' => ['required', 'exists:subscription_plans,id'],
            'billing_cycle' => ['required', 'in:monthly,yearly'],
        ]);

        $tenant = $request->user()->tenant;
        $plan = SubscriptionPlan::findOrFail($validated['subscription_plan_id']);
        $amount = $validated['billing_cycle'] === 'yearly' ? $plan->price_yearly : $plan->price_monthly;
        $reference = NumberGenerator::reference('SUBPAY');

        $tenantPayment = TenantPayment::create([
            'tenant_id' => $tenant->id,
            'subscription_plan_id' => $plan->id,
            'amount' => $amount,
            'billing_cycle' => $validated['billing_cycle'],
            'payment_channel' => 'gateway',
            'is_auto_billed' => false,
            'status' => 'pending',
            'reference' => $reference,
            'notes' => 'Tenant online subscription payment.',
        ]);

        $payment = GatewayPayment::create([
            'tenant_id' => $tenant->id,
            'user_id' => $request->user()->id,
            'payable_type' => TenantPayment::class,
            'payable_id' => $tenantPayment->id,
            'purpose' => GatewayPayment::PURPOSE_TENANT_SUBSCRIPTION,
            'provider' => $gateway->provider(),
            'amount' => $amount,
            'currency' => $tenant->settings['currency'] ?? 'NGN',
            'reference' => $reference,
            'status' => 'pending',
            'metadata' => [
                'plan' => $plan->name,
                'billing_cycle' => $validated['billing_cycle'],
            ],
        ]);

        $checkout = $gateway->initializeCheckout($payment, $tenant->email, route('tenant.billing.gateway.callback'));

        if (! $checkout['successful'] || ! $checkout['authorization_url']) {
            $payment->update(['status' => 'failed', 'gateway_response' => ['message' => $checkout['message']]]);
            $tenantPayment->update(['status' => 'rejected', 'rejected_reason' => $checkout['message']]);

            return back()->with('warning', $checkout['message']);
        }

        $payment->update(['checkout_url' => $checkout['authorization_url']]);

        return redirect()->away($checkout['authorization_url']);
    }

    public function completeGatewayPayment(Request $request, PaymentGatewayService $gateway, SubscriptionService $subscriptions): RedirectResponse
    {
        $reference = (string) $request->query('reference');

        if ($reference === '') {
            return redirect()->route('tenant.billing.index')->with('warning', 'Payment reference is missing.');
        }

        $payment = GatewayPayment::where('reference', $reference)
            ->where('purpose', GatewayPayment::PURPOSE_TENANT_SUBSCRIPTION)
            ->where('user_id', $request->user()->id)
            ->firstOrFail();

        if ($payment->status === 'paid') {
            return redirect()->route('tenant.billing.index')->with('status', 'Subscription payment already confirmed.');
        }

        $verified = $gateway->verifyCheckout($payment->reference);

        if (! $verified['successful'] || $verified['amount'] === null || round($verified['amount'], 2) !== round((float) $payment->amount, 2)) {
            $payment->update(['status' => 'failed', 'provider_reference' => $verified['reference'], 'gateway_response' => $verified['raw']]);

            return redirect()->route('tenant.billing.index')->with('warning', $verified['message']);
        }

        $autoBillingReference = $this->autoBillingReference($payment->provider, $verified['authorization_code']);

        DB::transaction(function () use ($payment, $verified, $subscriptions, $autoBillingReference): void {
            $tenantPayment = TenantPayment::whereKey($payment->payable_id)->lockForUpdate()->firstOrFail();
            $tenantPayment->update([
                'payment_channel' => $payment->provider,
                'gateway_reference' => $verified['reference'],
            ]);

            $subscriptions->approvePayment($tenantPayment);
            $tenant = $tenantPayment->tenant()->withoutGlobalScopes()->lockForUpdate()->firstOrFail();

            if ($autoBillingReference) {
                $tenant->update([
                    'auto_billing_enabled' => true,
                    'auto_billing_cycle' => $tenantPayment->billing_cycle,
                    'auto_billing_method' => 'gateway_authorization',
                    'auto_billing_reference' => $autoBillingReference,
                    'auto_billing_status' => 'active',
                    'auto_billing_started_at' => $tenant->auto_billing_started_at ?? now(),
                    'auto_billing_last_charged_at' => now(),
                    'auto_billing_next_charge_at' => $tenant->subscription_ends_at,
                    'auto_billing_failed_at' => null,
                    'auto_billing_failure_reason' => null,
                ]);
            }

            $payment->update([
                'status' => 'paid',
                'provider_reference' => $verified['reference'],
                'authorization_code' => $verified['authorization_code'],
                'gateway_response' => $verified['raw'],
                'paid_at' => now(),
            ]);
        });

        return redirect()->route('tenant.billing.index')->with('status', $autoBillingReference
            ? 'Subscription payment confirmed, activated, and automatic renewal is now on.'
            : 'Subscription payment confirmed and activated. Automatic renewal needs a reusable card authorization before it can run.');
    }

    public function updateAutoRenewal(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'auto_billing_enabled' => ['nullable', 'boolean'],
            'auto_billing_cycle' => ['required_if:auto_billing_enabled,1', 'nullable', 'in:monthly,yearly'],
            'auto_billing_method' => ['required_if:auto_billing_enabled,1', 'nullable', 'in:card,bank,gateway_authorization'],
            'auto_billing_reference' => ['required_if:auto_billing_enabled,1', 'nullable', 'string', 'max:255'],
        ]);

        $tenant = $request->user()->tenant;
        $enabled = $request->boolean('auto_billing_enabled');
        $nextChargeAt = $tenant->subscription_ends_at ?? $tenant->trial_ends_at ?? now()->addDays(14);

        $tenant->update([
            'auto_billing_enabled' => $enabled,
            'auto_billing_cycle' => $enabled ? $validated['auto_billing_cycle'] : null,
            'auto_billing_method' => $enabled ? $validated['auto_billing_method'] : null,
            'auto_billing_reference' => $enabled ? $validated['auto_billing_reference'] : null,
            'auto_billing_status' => $enabled ? 'active' : 'inactive',
            'auto_billing_started_at' => $enabled ? ($tenant->auto_billing_started_at ?? now()) : null,
            'auto_billing_next_charge_at' => $enabled ? $nextChargeAt : null,
            'auto_billing_failed_at' => null,
            'auto_billing_failure_reason' => null,
        ]);

        return back()->with('status', $enabled
            ? 'Automatic '.$validated['auto_billing_cycle'].' renewal is active.'
            : 'Automatic renewal has been turned off.');
    }

    private function autoBillingReference(?string $provider, ?string $authorizationCode): ?string
    {
        if (! $authorizationCode) {
            return null;
        }

        $provider = strtolower((string) $provider);

        return $provider !== ''
            ? $provider.':'.$authorizationCode
            : $authorizationCode;
    }
}
