<?php

namespace App\Services\Subscriptions;

use App\Enums\PaymentStatus;
use App\Enums\SubscriptionStatus;
use App\Models\Appointment;
use App\Models\Business;
use App\Models\Invoice;
use App\Models\Plan;
use App\Models\ScheduledReminder;
use App\Models\StaffProfile;
use App\Models\Subscription;
use App\Models\SubscriptionPayment;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class SubscriptionService
{
    public function activePlans(): Collection
    {
        return Plan::query()
            ->active()
            ->orderBy('sort_order')
            ->orderBy('monthly_price')
            ->get();
    }

    public function currentSubscription(Business $business): ?Subscription
    {
        return $business->subscriptions()
            ->with(['plan', 'payments' => fn ($query) => $query->latest()])
            ->latest('starts_at')
            ->latest('id')
            ->first();
    }

    public function state(Business $business): array
    {
        $subscription = $this->currentSubscription($business);

        return [
            'subscription' => $subscription,
            'is_active' => $subscription?->isActiveLike() ?? false,
            'is_expired' => ! $subscription || $subscription->isExpired(),
            'is_trial' => $subscription?->isTrial() ?? false,
            'trial_ending' => $subscription?->trialEndsSoon() ?? false,
            'trial_days_left' => $subscription?->trial_ends_at?->isFuture()
                ? now()->startOfDay()->diffInDays($subscription->trial_ends_at->copy()->startOfDay(), false)
                : null,
        ];
    }

    public function usage(Business $business, ?Subscription $subscription = null): array
    {
        $subscription ??= $this->currentSubscription($business);
        $plan = $subscription?->plan;

        $monthStart = now()->startOfMonth();
        $monthEnd = now()->endOfMonth();

        $rows = [
            [
                'key' => 'staff',
                'label' => 'Staff',
                'used' => StaffProfile::query()->forBusiness($business)->count(),
                'allowed' => $plan?->max_staff,
                'note' => 'Team members available in this business.',
            ],
            [
                'key' => 'services',
                'label' => 'Services',
                'used' => $business->services()->count(),
                'allowed' => $plan?->max_services,
                'note' => 'Bookable services in your catalog.',
            ],
            [
                'key' => 'appointments',
                'label' => 'Appointments',
                'used' => Appointment::query()->forBusiness($business)->whereBetween('starts_at', [$monthStart, $monthEnd])->count(),
                'allowed' => $plan?->max_appointments_per_month,
                'note' => 'Appointments used this month.',
            ],
            [
                'key' => 'reminders',
                'label' => 'Reminders',
                'used' => ScheduledReminder::query()->forBusiness($business)->whereBetween('created_at', [$monthStart, $monthEnd])->count(),
                'allowed' => $plan?->max_reminders_per_month,
                'note' => 'Scheduled reminders created this month.',
            ],
            [
                'key' => 'invoices',
                'label' => 'Invoices',
                'used' => Invoice::query()->forBusiness($business)->whereBetween('issue_date', [$monthStart, $monthEnd])->count(),
                'allowed' => $plan?->max_invoices_per_month,
                'note' => 'Invoices issued this month.',
            ],
        ];

        return collect($rows)->map(function (array $row): array {
            $allowed = $row['allowed'];
            $percent = $allowed ? min(100, (int) round(($row['used'] / $allowed) * 100)) : 0;

            return $row + [
                'allowed_display' => $allowed ? number_format($allowed) : 'Unlimited',
                'is_unlimited' => $allowed === null,
                'percent' => $percent,
                'is_near_limit' => $allowed !== null && $percent >= 85,
                'is_over_limit' => $allowed !== null && $row['used'] > $allowed,
            ];
        })->all();
    }

    public function hasFeature(Business $business, string $feature): bool
    {
        $plan = $this->currentSubscription($business)?->plan;

        return $plan?->featureEnabled($feature) ?? false;
    }

    public function withinLimit(Business $business, string $key, int $additional = 1): bool
    {
        $usage = collect($this->usage($business))->firstWhere('key', $key);

        if (! $usage || $usage['is_unlimited']) {
            return true;
        }

        return ($usage['used'] + $additional) <= $usage['allowed'];
    }

    public function changePlan(Business $business, Plan $plan, string $billingCycle = 'monthly'): Subscription
    {
        return DB::transaction(function () use ($business, $plan, $billingCycle): Subscription {
            $price = $billingCycle === 'yearly' ? $plan->yearly_price : $plan->monthly_price;

            $business->subscriptions()
                ->whereIn('status', [SubscriptionStatus::Active->value, SubscriptionStatus::Trial->value, SubscriptionStatus::PastDue->value])
                ->update(['status' => SubscriptionStatus::Cancelled->value, 'cancelled_at' => now(), 'ends_at' => now()]);

            return Subscription::query()->create([
                'business_id' => $business->id,
                'plan_id' => $plan->id,
                'status' => $price > 0 ? SubscriptionStatus::Active->value : SubscriptionStatus::Trial->value,
                'billing_cycle' => $billingCycle,
                'starts_at' => now(),
                'trial_ends_at' => $plan->trial_days > 0 ? now()->addDays($plan->trial_days) : null,
                'renews_at' => $price > 0 ? ($billingCycle === 'yearly' ? now()->addYear() : now()->addMonth()) : null,
                'price' => $price,
                'currency' => $business->currency ?: 'USD',
                'gateway' => null,
                'usage_snapshot' => collect($this->usage($business))->mapWithKeys(fn (array $row) => [$row['key'] => $row['used']])->all(),
                'meta' => [
                    'note' => 'Manual plan switch placeholder. Recurring billing will be connected later.',
                ],
            ]);
        });
    }

    public function billingHistory(Business $business): array
    {
        return SubscriptionPayment::query()
            ->with('plan')
            ->forBusiness($business)
            ->latest()
            ->get()
            ->map(fn (SubscriptionPayment $payment) => [
                'invoice' => 'SUB-'.str_pad((string) $payment->id, 5, '0', STR_PAD_LEFT),
                'date' => $payment->paid_at?->format('M d, Y') ?? $payment->created_at->format('M d, Y'),
                'plan' => $payment->plan?->name ?? 'Plan',
                'amount' => $this->money((float) $payment->amount, $payment->currency),
                'status' => $payment->status instanceof PaymentStatus ? $payment->status->label() : Str::headline((string) $payment->status),
                'receipt_url' => $payment->receipt_url,
            ])
            ->all();
    }

    public function planCards(?Business $business = null): array
    {
        $currentPlanId = $business ? $this->currentSubscription($business)?->plan_id : null;

        return $this->activePlans()->map(fn (Plan $plan) => [
            'id' => $plan->id,
            'name' => $plan->name,
            'monthly' => $plan->monthlyDisplay(),
            'yearly' => $plan->yearlyDisplay(),
            'monthly_price' => $plan->monthly_price,
            'yearly_price' => $plan->yearly_price,
            'trial_days' => $plan->trial_days,
            'badge' => $plan->slug === 'professional' ? 'Recommended' : $plan->status_label,
            'description' => $plan->description ?: "Booklio {$plan->name} plan.",
            'is_current' => $currentPlanId === $plan->id,
            'is_recommended' => $plan->slug === 'professional',
            'features' => $this->featureBullets($plan),
            'limits' => [
                'staff' => $this->limitText($plan->max_staff),
                'services' => $this->limitText($plan->max_services),
                'appointments' => $this->limitText($plan->max_appointments_per_month),
                'reminders' => $this->limitText($plan->max_reminders_per_month),
                'invoices' => $this->limitText($plan->max_invoices_per_month),
            ],
        ])->all();
    }

    public function businessContext(Business $business): array
    {
        $subscription = $this->currentSubscription($business);
        $plan = $subscription?->plan;
        $state = $this->state($business);

        return [
            'subscription' => $subscription,
            'subscriptionState' => $state,
            'currentPlan' => [
                'name' => $plan?->name ?? 'No active plan',
                'cycle' => $subscription ? Str::headline($subscription->billing_cycle) : 'Not selected',
                'status' => $subscription?->status_label ?? 'Expired',
                'renewal' => $subscription?->renews_at?->format('M d, Y') ?? 'Not scheduled',
            ],
            'usageLimits' => $this->usage($business, $subscription),
            'plans' => $this->planCards($business),
            'billingHistory' => $this->billingHistory($business),
            'planBenefits' => $plan ? $this->featureBullets($plan) : [],
        ];
    }

    private function featureBullets(Plan $plan): array
    {
        return array_values(array_filter([
            "{$this->limitText($plan->max_staff)} staff members",
            "{$this->limitText($plan->max_services)} services",
            "{$this->limitText($plan->max_appointments_per_month)} appointments / month",
            "{$this->limitText($plan->max_reminders_per_month)} reminders / month",
            $plan->invoice_module_enabled ? 'Invoice module enabled' : null,
            $plan->whatsapp_reminder_enabled ? 'WhatsApp reminders enabled' : null,
            $plan->email_reminder_enabled ? 'Email reminders enabled' : null,
            $plan->reports_enabled ? 'Reports enabled' : null,
            $plan->custom_branding_enabled ? 'Custom branding enabled' : null,
        ]));
    }

    private function limitText(?int $limit): string
    {
        return $limit === null ? 'Unlimited' : number_format($limit);
    }

    private function money(float $amount, string $currency = 'USD'): string
    {
        return match ($currency) {
            'USD' => '$'.number_format($amount, 2),
            default => $currency.' '.number_format($amount, 2),
        };
    }
}
