<?php

namespace App\Filament\Widgets;

use App\Models\Subscription;
use Filament\Widgets\ChartWidget;
use Illuminate\Support\Facades\DB;

class CustomerLifetimeValue extends ChartWidget
{
    protected static ?int $sort = 5;

    protected int|string|array $columnSpan = 'full';

    public function getHeading(): string
    {
        return 'Customer Lifetime Value Analysis';
    }

    protected function getData(): array
    {
        $ltvByCohort = $this->getLTVByCohort();
        $ltvByProvider = $this->getLTVByProvider();
        $ltvByPlan = $this->getLTVByPlan();

        return [
            'datasets' => [
                [
                    'label' => 'Average LTV by Cohort ($)',
                    'data' => array_values($ltvByCohort),
                    'borderColor' => 'rgba(34, 197, 94, 1)',
                    'backgroundColor' => 'rgba(34, 197, 94, 0.1)',
                    'fill' => true,
                    'tension' => 0.4,
                ],
            ],
            'labels' => array_keys($ltvByCohort),
        ];
    }

    protected function getType(): string
    {
        return 'bar';
    }

    protected function getOptions(): array
    {
        return [
            'responsive' => true,
            'plugins' => [
                'legend' => [
                    'position' => 'top',
                ],
                'tooltip' => [
                    'callbacks' => [
                        'label' => 'function(context) {
                            let label = context.dataset.label || "";
                            if (label) {
                                label += ": ";
                            }
                            if (context.parsed.y !== null) {
                                label += "$" + context.parsed.y.toFixed(2);
                            }
                            return label;
                        }',
                    ],
                ],
            ],
            'scales' => [
                'y' => [
                    'beginAtZero' => true,
                    'ticks' => [
                        'callback' => 'function(value) {
                            return "$" + value;
                        }',
                    ],
                ],
            ],
        ];
    }

    private function getLTVByCohort(): array
    {
        $cohorts = [];

        // Get cohorts by signup month (last 6 months)
        for ($i = 5; $i >= 0; $i--) {
            $month = now()->subMonths($i);
            $monthStart = $month->copy()->startOfMonth();
            $monthEnd = $month->copy()->endOfMonth();

            // Users who signed up in this month
            $cohortUsers = Subscription::query()
                ->select('user_id')
                ->whereBetween('created_at', [$monthStart, $monthEnd])
                ->distinct()
                ->pluck('user_id');

            if ($cohortUsers->isEmpty()) {
                $cohorts[$month->format('M Y')] = 0;

                continue;
            }

            // Calculate LTV for this cohort
            $totalRevenue = Subscription::query()
                ->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
                ->whereIn('subscriptions.user_id', $cohortUsers)
                ->where('subscriptions.status', 'active')
                ->sum('plans.price');

            // Estimate LTV based on current MRR and average subscription length
            $avgSubscriptionLength = $this->getAverageSubscriptionLength();
            $cohortSize = $cohortUsers->count();
            $avgMRRPerUser = $cohortSize > 0 ? $totalRevenue / $cohortSize : 0;
            $ltv = $avgMRRPerUser * $avgSubscriptionLength;

            $cohorts[$month->format('M Y')] = round($ltv, 2);
        }

        return $cohorts;
    }

    private function getLTVByProvider(): array
    {
        return Subscription::query()
            ->select(
                'provider',
                DB::raw('AVG(plans.price) as avg_monthly_price'),
                DB::raw('COUNT(*) as total_subscriptions')
            )
            ->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
            ->where('subscriptions.status', 'active')
            ->groupBy('provider')
            ->orderBy('avg_monthly_price', 'desc')
            ->get()
            ->mapWithKeys(function ($item) {
                $avgLength = $this->getAverageSubscriptionLengthByProvider($item->provider);
                $ltv = $item->avg_monthly_price * $avgLength;

                return [$item->provider => round($ltv, 2)];
            })
            ->toArray();
    }

    private function getLTVByPlan(): array
    {
        return Subscription::query()
            ->select(
                'plans.name as plan_name',
                DB::raw('plans.price'),
                DB::raw('COUNT(*) as total_subscriptions')
            )
            ->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
            ->where('subscriptions.status', 'active')
            ->groupBy('plans.id', 'plans.name', 'plans.price')
            ->orderBy('plans.price', 'desc')
            ->limit(10)
            ->get()
            ->mapWithKeys(function ($item) {
                $avgLength = $this->getAverageSubscriptionLengthByPlan($item->plan_name);
                $ltv = $item->price * $avgLength;

                return [$item->plan_name => round($ltv, 2)];
            })
            ->toArray();
    }

    private function getAverageSubscriptionLength(): float
    {
        // Average subscription length in months (simplified calculation)
        return 12; // Could be calculated based on historical data
    }

    private function getAverageSubscriptionLengthByProvider(string $provider): float
    {
        // Provider-specific average subscription length
        $providerLengths = [
            'stripe' => 14,
            'lemon_squeezy' => 12,
            'polar' => 10,
            'oxapay' => 8,
            'crypto' => 6,
            'activation_key' => 24,
        ];

        return $providerLengths[$provider] ?? 12;
    }

    private function getAverageSubscriptionLengthByPlan(string $planName): float
    {
        // Plan-specific average subscription length (could be based on plan tier)
        return 12; // Simplified, could be more sophisticated
    }

    private function getTopQuartileLTV(): float
    {
        $ltvs = Subscription::query()
            ->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
            ->where('subscriptions.status', 'active')
            ->selectRaw('plans.price * ? as ltv', [$this->getAverageSubscriptionLength()])
            ->pluck('ltv')
            ->sort()
            ->values();

        $quartileIndex = (int) ($ltvs->count() * 0.75);

        return $ltvs->get($quartileIndex, 0);
    }

    private function getBottomQuartileLTV(): float
    {
        $ltvs = Subscription::query()
            ->join('plans', 'subscriptions.plan_id', '=', 'plans.id')
            ->where('subscriptions.status', 'active')
            ->selectRaw('plans.price * ? as ltv', [$this->getAverageSubscriptionLength()])
            ->pluck('ltv')
            ->sort()
            ->values();

        $quartileIndex = (int) ($ltvs->count() * 0.25);

        return $ltvs->get($quartileIndex, 0);
    }
}
