<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Mail\SubscriptionInvoiceMail;
use App\Models\PlatformPayment;
use App\Models\PlatformSetting;
use App\Models\Subscription;
use App\Models\SubscriptionPlan;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

class BillingController extends Controller
{
    /**
     * Return the authenticated user's current subscription + matching plan features.
     */
    public function current(Request $request)
    {
        $org = $request->user()->organization;

        if (!$org) {
            return response()->json(['subscription' => null, 'plan_details' => null]);
        }

        $subscription = $org->subscription;
        $planDetails  = null;

        if ($subscription) {
            $planDetails = SubscriptionPlan::where('name', $subscription->plan)->first();
        }

        // An org that has any subscription record (trial, active, expired, or
        // cancelled) is no longer eligible for a new free trial.
        $trialUsed = Subscription::where('organization_id', $org->id)->exists();

        return response()->json([
            'subscription' => $subscription,
            'plan_details' => $planDetails,
            'trial_used'   => $trialUsed,
            'organization' => [
                'id'        => $org->id,
                'name'      => $org->name,
                'email'     => $org->email,
                'is_active' => $org->is_active,
            ],
        ]);
    }

    /**
     * Return all active subscription plans (excluding the free plan) for the
     * upgrade/plan comparison view. Changes made by the platform admin to plans
     * are immediately visible here.
     */
    public function plans()
    {
        $plans = SubscriptionPlan::where('is_active', true)
            ->where('name', '!=', 'free')
            ->orderBy('sort_order')
            ->orderBy('monthly_price')
            ->get();

        return response()->json($plans);
    }

    /**
     * Return platform payments (invoices) for the authenticated user's organization.
     */
    public function payments(Request $request)
    {
        $orgId = $request->user()->organization_id;

        if (!$orgId) {
            return response()->json(['data' => [], 'total' => 0]);
        }

        $payments = PlatformPayment::where('organization_id', $orgId)
            ->orderByDesc('created_at')
            ->paginate(15);

        return response()->json($payments);
    }

    /**
     * Self-service plan activation — allows a tenant to choose and activate a subscription plan.
     * If the plan has trial_days, a trial is started. Otherwise the subscription is activated directly.
     */
    public function subscribe(Request $request)
    {
        $validated = $request->validate([
            'plan'          => 'required|string|max:50',
            'billing_cycle' => 'required|in:monthly,quarterly,semi_yearly,yearly',
        ]);

        $org = $request->user()->organization;

        if (! $org) {
            return response()->json(['message' => 'Organization not found.'], 404);
        }

        if (! $org->is_active) {
            return response()->json(['message' => 'Your organization account is suspended. Please contact support.'], 403);
        }

        $plan = SubscriptionPlan::where('name', $validated['plan'])
            ->where('is_active', true)
            ->first();

        if (! $plan) {
            return response()->json(['message' => 'The selected plan is not available.'], 404);
        }

        // Cancel any existing active/trial subscription for this org
        Subscription::where('organization_id', $org->id)
            ->whereIn('status', ['active', 'trial'])
            ->update(['status' => 'cancelled', 'cancelled_at' => now()]);

        $now          = now();
        $cycle        = $validated['billing_cycle'];
        $amount       = $this->computeAmount($plan, $cycle);
        $periodEnd    = $this->computePeriodEnd($now, $cycle);
        $trialAlreadyUsed = Subscription::where('organization_id', $org->id)->exists();

        $isTrial = $plan->trial_days > 0 && ! $trialAlreadyUsed;
        $status  = $isTrial ? 'trial' : 'active';
        $trialEndsAt  = $isTrial ? $now->copy()->addDays($plan->trial_days) : null;

        $subscription = Subscription::create([
            'organization_id'      => $org->id,
            'plan'                 => $plan->name,
            'status'               => $status,
            'amount'               => $amount,
            'billing_cycle'        => $cycle,
            'trial_ends_at'        => $trialEndsAt,
            'current_period_start' => $now,
            'current_period_end'   => $isTrial ? ($trialEndsAt ?? $periodEnd) : $periodEnd,
            'notes'                => 'Self-activated via billing portal',
        ]);

        // Generate a pending invoice for paid activations
        if (! $isTrial && $amount > 0) {
            PlatformPayment::create([
                'organization_id' => $org->id,
                'subscription_id' => $subscription->id,
                'invoice_number'  => 'INV-' . strtoupper(substr(uniqid('', true), -8)),
                'amount'          => $amount,
                'plan'            => $plan->name,
                'payment_method'  => 'manual',
                'status'          => 'pending',
                'description'     => ucfirst($cycle) . ' subscription — ' . $plan->label . ' plan',
            ]);
        }

        $message = $isTrial
            ? "Your {$plan->trial_days}-day free trial for the {$plan->label} plan has started!"
            : "You have successfully activated the {$plan->label} plan.";

        return response()->json([
            'message'      => $message,
            'subscription' => $subscription,
            'plan_details' => $plan,
        ]);
    }

    /**
     * Initialize a Paystack transaction for a paid plan upgrade.
     * Returns the access_code and reference needed to open the Paystack popup.
     */
    public function initiatePayment(Request $request)
    {
        $validated = $request->validate([
            'plan'          => 'required|string|max:50',
            'billing_cycle' => 'required|in:monthly,quarterly,semi_yearly,yearly',
        ]);

        $org = $request->user()->organization;

        if (! $org) {
            return response()->json(['message' => 'Organization not found.'], 404);
        }

        $plan = SubscriptionPlan::where('name', $validated['plan'])
            ->where('is_active', true)
            ->first();

        if (! $plan) {
            return response()->json(['message' => 'The selected plan is not available.'], 404);
        }

        $cycle      = $validated['billing_cycle'];
        $amount     = $this->computeAmount($plan, $cycle);
        $amountKobo = (int) round($amount * 100);

        $secretKey = config('payment.paystack_secret_key');
        if (empty($secretKey)) {
            return response()->json(['message' => 'Payment gateway is not configured. Please contact support.'], 503);
        }

        $paystackResponse = Http::withToken($secretKey)
            ->post('https://api.paystack.co/transaction/initialize', [
                'email'    => $org->email,
                'amount'   => $amountKobo,
                'currency' => 'NGN',
                'metadata' => [
                    'org_id'        => $org->id,
                    'plan'          => $plan->name,
                    'billing_cycle' => $cycle,
                    'renew'         => $request->boolean('renew'),
                ],
                'channels' => ['card', 'bank', 'ussd', 'bank_transfer'],
            ]);

        if (! $paystackResponse->successful() || ! $paystackResponse->json('status')) {
            return response()->json([
                'message' => $paystackResponse->json('message') ?? 'Failed to initialize payment.',
            ], 502);
        }

        return response()->json([
            'access_code'       => $paystackResponse->json('data.access_code'),
            'authorization_url' => $paystackResponse->json('data.authorization_url'),
            'reference'         => $paystackResponse->json('data.reference'),
            'public_key'        => config('payment.paystack_public_key'),
            'email'             => $org->email,
            'amount'            => $amountKobo,
        ]);
    }

    /**
     * Verify a completed Paystack transaction and activate the subscription.
     * Called by the frontend after the Paystack popup reports success.
     */
    public function verifyPayment(Request $request)
    {
        $validated = $request->validate(['reference' => 'required|string|max:100']);
        $reference = $validated['reference'];

        $secretKey = config('payment.paystack_secret_key');
        if (empty($secretKey)) {
            return response()->json(['message' => 'Payment gateway is not configured.'], 503);
        }

        $org = $request->user()->organization;
        if (! $org) {
            return response()->json(['message' => 'Organization not found.'], 404);
        }

        // Idempotency: if already processed (e.g. webhook fired first), return
        // the current subscription so the frontend can update without showing an error.
        $existingPayment = PlatformPayment::where('transaction_id', $reference)
            ->where('status', 'paid')
            ->first();

        if ($existingPayment) {
            $subscription = $org->fresh('subscription')->subscription;
            $plan         = $subscription ? SubscriptionPlan::where('name', $subscription->plan)->first() : null;
            return response()->json([
                'message'      => 'Your ' . ($plan?->label ?? $subscription?->plan ?? '') . ' plan is now active.',
                'subscription' => $subscription,
                'plan_details' => $plan,
            ]);
        }

        $paystackResponse = Http::withToken($secretKey)
            ->get("https://api.paystack.co/transaction/verify/{$reference}");

        if (! $paystackResponse->successful() || ! $paystackResponse->json('status')) {
            return response()->json(['message' => 'Payment verification failed.'], 422);
        }

        $data = $paystackResponse->json('data');

        if (($data['status'] ?? '') !== 'success') {
            return response()->json(['message' => 'Payment was not successful.'], 422);
        }

        $metadata = $data['metadata'] ?? [];
        $planName = $metadata['plan'] ?? null;
        $cycle    = $metadata['billing_cycle'] ?? 'monthly';

        // Security: ensure this transaction belongs to the authenticated org.
        $metaOrgId = $metadata['org_id'] ?? null;
        if ($metaOrgId && (int) $metaOrgId !== $org->id) {
            Log::warning('verifyPayment: org_id mismatch.', [
                'auth_org' => $org->id, 'meta_org' => $metaOrgId, 'ref' => $reference,
            ]);
            return response()->json(['message' => 'Payment reference does not match your account.'], 403);
        }

        if (! $planName) {
            return response()->json(['message' => 'Payment metadata is missing plan information.'], 422);
        }

        $plan       = SubscriptionPlan::where('name', $planName)->first();
        $amountPaid = ($data['amount'] ?? 0) / 100;

        $isRenewal  = (bool) ($metadata['renew'] ?? false);

        // For a renewal on the same active plan, extend from the current period end
        // so the subscriber does not lose any remaining time.
        $now       = now();
        $activeSub = $org->refresh()->subscription;
        if ($isRenewal && $activeSub && $activeSub->status === 'active' && $activeSub->plan === $planName) {
            $base        = \Carbon\Carbon::parse($activeSub->current_period_end)->max($now);
            $periodStart = $base->copy();
            $periodEnd   = $this->computePeriodEnd($base, $cycle);
        } else {
            $periodStart = $now;
            $periodEnd   = $this->computePeriodEnd($now, $cycle);
        }

        // Cancel any existing active/trial subscription
        Subscription::where('organization_id', $org->id)
            ->whereIn('status', ['active', 'trial'])
            ->update(['status' => 'cancelled', 'cancelled_at' => now()]);

        $subscription = Subscription::create([
            'organization_id'      => $org->id,
            'plan'                 => $planName,
            'status'               => 'active',
            'amount'               => $amountPaid,
            'billing_cycle'        => $cycle,
            'current_period_start' => $periodStart,
            'current_period_end'   => $periodEnd,
            'notes'                => ($isRenewal ? 'Renewed' : 'Activated') . ' via Paystack ref: ' . $reference,
        ]);

        $payment = PlatformPayment::create([
            'organization_id' => $org->id,
            'subscription_id' => $subscription->id,
            'invoice_number'  => 'PAY-' . strtoupper(substr($reference, 0, 12)),
            'amount'          => $amountPaid,
            'plan'            => $planName,
            'payment_method'  => $data['channel'] ?? 'card',
            'payment_gateway' => 'paystack',
            'transaction_id'  => $reference,
            'status'          => 'paid',
            'description'     => ucfirst($cycle) . ' subscription — ' . ($plan?->label ?? $planName) . ' plan',
            'paid_at'         => now(),
        ]);

        // Send receipt email to the organisation's registered address.
        try {
            $payment->load('organization', 'subscription');
            Mail::to($org->email)->queue(new SubscriptionInvoiceMail($payment));
        } catch (\Throwable $e) {
            Log::warning('Failed to queue subscription receipt email.', [
                'org_id'    => $org->id,
                'reference' => $reference,
                'error'     => $e->getMessage(),
            ]);
        }

        return response()->json([
            'message'      => 'Payment successful! Your ' . ($plan?->label ?? $planName) . ' plan is now active.',
            'subscription' => $subscription,
            'plan_details' => $plan,
        ]);
    }

    /**
     * Record a pending bank-transfer subscription request and return bank details.
     * The actual subscription is activated manually by the platform admin after
     * payment is confirmed.
     */
    public function bankTransferRequest(Request $request)
    {
        $validated = $request->validate([
            'plan'          => 'required|string|max:50',
            'billing_cycle' => 'required|in:monthly,quarterly,semi_yearly,yearly',
        ]);

        $org = $request->user()->organization;

        if (! $org) {
            return response()->json(['message' => 'Organization not found.'], 404);
        }

        $plan = SubscriptionPlan::where('name', $validated['plan'])
            ->where('is_active', true)
            ->first();

        if (! $plan) {
            return response()->json(['message' => 'The selected plan is not available.'], 404);
        }

        $cycle     = $validated['billing_cycle'];
        $amount    = $this->computeAmount($plan, $cycle);
        $reference = 'BT-' . strtoupper(uniqid('', false));

        // Replace any previous pending bank-transfer for this org (idempotent)
        PlatformPayment::where('organization_id', $org->id)
            ->where('payment_method', 'bank_transfer')
            ->where('status', 'pending')
            ->delete();

        PlatformPayment::create([
            'organization_id' => $org->id,
            'invoice_number'  => 'INV-' . strtoupper(substr(uniqid('', true), -8)),
            'transaction_id'  => $reference,
            'amount'          => $amount,
            'plan'            => $plan->name,
            'payment_method'  => 'bank_transfer',
            'status'          => 'pending',
            'description'     => ucfirst($cycle) . ' ' . ($request->boolean('renew') ? 'renewal' : 'subscription') . ' — ' . $plan->label . ' plan (awaiting bank transfer)',
        ]);

        $defaultInstructions = 'Please transfer the exact amount shown and use the reference code below. '
            . 'Send proof of payment (screenshot or receipt) to our support email. '
            . 'Your subscription will be activated within 1–2 business days after confirmation.';

        return response()->json([
            'reference'    => $reference,
            'amount'       => $amount,
            'plan_label'   => $plan->label,
            'cycle'        => $cycle,
            'bank_details' => [
                'bank_name'      => PlatformSetting::get('bank_name', ''),
                'account_number' => PlatformSetting::get('account_number', ''),
                'account_name'   => PlatformSetting::get('account_name', ''),
                'sort_code'      => PlatformSetting::get('bank_sort_code', ''),
                'instructions'   => PlatformSetting::get('bank_transfer_instructions', $defaultInstructions),
            ],
            'message' => 'Bank transfer details loaded. Please complete your transfer using the details below.',
        ]);
    }

    /**
     * Download a PDF invoice for a specific payment — tenant-scoped (org must own the payment).
     */
    public function downloadInvoice(Request $request, PlatformPayment $payment)    {
        $orgId = $request->user()->organization_id;

        if ($payment->organization_id !== $orgId) {
            abort(403, 'You do not have permission to access this invoice.');
        }

        $payment->load('organization', 'subscription');

        $pdf = Pdf::loadView('invoices.subscription', [
            'payment' => $payment,
            'org'     => $payment->organization,
        ])->setPaper('a4', 'portrait');

        return $pdf->download("invoice-{$payment->invoice_number}.pdf");
    }

    // ── Private helpers ──────────────────────────────────────────────────────

    private function computeAmount(SubscriptionPlan $plan, string $cycle): float
    {
        return match ($cycle) {
            'yearly'      => $plan->yearly_price > 0 ? $plan->yearly_price : round($plan->monthly_price * 12, 2),
            'semi_yearly' => round($plan->monthly_price * 6, 2),
            'quarterly'   => round($plan->monthly_price * 3, 2),
            default       => $plan->monthly_price,
        };
    }

    private function computePeriodEnd(\Carbon\Carbon $from, string $cycle): \Carbon\Carbon
    {
        return match ($cycle) {
            'yearly'      => $from->copy()->addYear(),
            'semi_yearly' => $from->copy()->addMonths(6),
            'quarterly'   => $from->copy()->addMonths(3),
            default       => $from->copy()->addMonth(),
        };
    }
}
