<?php

declare(strict_types=1);

namespace App\Services\Platform;

use App\Models\Company;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\SubscriptionRequest;
use App\Services\Dashboard\DashboardCacheService;
use App\ValueObjects\Money;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * Servicio de métricas SaaS para Super Admin.
 *
 * Proporciona métricas de nivel plataforma:
 * - MRR/ARR
 * - Churn rate
 * - Trial conversion
 * - Suscripciones por estado/plan
 * - Uso de límites
 *
 * IMPORTANTE: Este servicio NO usa company_id filtering.
 * Solo debe ser accedido por Super Admins.
 */
class SaaSMetricsService
{
    /** Meses en cada ciclo de facturación */
    private const BILLING_CYCLE_MONTHS = [
        'monthly' => 1,
        'quarterly' => 3,
        'semiannual' => 6,
        'annual' => 12,
    ];

    public function __construct(
        private DashboardCacheService $cache
    ) {}

    /**
     * Resumen completo de métricas de plataforma.
     */
    public function getPlatformSummary(): array
    {
        return $this->cache->remember(0, 'platform.summary', function () {
            return [
                'companies' => $this->getCompanyStats(),
                'subscriptions' => $this->getSubscriptionStats(),
                'revenue' => $this->getRevenueMetrics(),
                'growth' => $this->getGrowthMetrics(),
                'pending_requests' => SubscriptionRequest::pendingCount(),
            ];
        }, 'daily');
    }

    /**
     * Estadísticas de companies.
     */
    public function getCompanyStats(): array
    {
        return $this->cache->remember(0, 'platform.companies', function () {
            $total = Company::count();
            $active = Company::where('status', 'active')->count();
            $suspended = Company::where('status', 'suspended')->count();
            $inactive = Company::where('status', 'inactive')->count();

            // Companies creadas este mes
            $newThisMonth = Company::where('created_at', '>=', now()->startOfMonth())->count();

            // Companies creadas mes anterior
            $newLastMonth = Company::whereBetween('created_at', [
                now()->subMonth()->startOfMonth(),
                now()->subMonth()->endOfMonth(),
            ])->count();

            $growthRate = $newLastMonth > 0
                ? round((($newThisMonth - $newLastMonth) / $newLastMonth) * 100, 1)
                : ($newThisMonth > 0 ? 100 : 0);

            return [
                'total' => $total,
                'active' => $active,
                'suspended' => $suspended,
                'inactive' => $inactive,
                'new_this_month' => $newThisMonth,
                'new_last_month' => $newLastMonth,
                'growth_rate' => $growthRate,
            ];
        }, 'daily');
    }

    /**
     * Estadísticas de suscripciones por estado.
     */
    public function getSubscriptionStats(): array
    {
        return $this->cache->remember(0, 'platform.subscriptions', function () {
            $statuses = [
                Subscription::STATUS_ACTIVE,
                Subscription::STATUS_TRIAL,
                Subscription::STATUS_GRACE,
                Subscription::STATUS_EXPIRED,
                Subscription::STATUS_CANCELED,
                Subscription::STATUS_SUSPENDED,
            ];

            $byStatus = [];
            $total = 0;

            foreach ($statuses as $status) {
                $count = Subscription::where('status', $status)->count();
                $byStatus[$status] = [
                    'count' => $count,
                    'label' => $this->getStatusLabel($status),
                    'color' => $this->getStatusColor($status),
                ];
                $total += $count;
            }

            // Calcular porcentajes
            foreach ($byStatus as $status => &$data) {
                $data['percentage'] = $total > 0
                    ? round(($data['count'] / $total) * 100, 1)
                    : 0;
            }

            return [
                'total' => $total,
                'by_status' => $byStatus,
                'active_total' => $byStatus[Subscription::STATUS_ACTIVE]['count']
                    + $byStatus[Subscription::STATUS_TRIAL]['count']
                    + $byStatus[Subscription::STATUS_GRACE]['count'],
            ];
        }, 'daily');
    }

    /**
     * Suscripciones por plan.
     */
    public function getSubscriptionsByPlan(): Collection
    {
        return $this->cache->remember(0, 'platform.subscriptions_by_plan', function () {
            return Subscription::query()
                ->select('plan_id', DB::raw('COUNT(*) as count'))
                ->whereIn('status', [
                    Subscription::STATUS_ACTIVE,
                    Subscription::STATUS_TRIAL,
                    Subscription::STATUS_GRACE,
                ])
                ->groupBy('plan_id')
                ->with('plan:id,name,price,badge_color')
                ->get()
                ->map(function ($item) {
                    return [
                        'plan_id' => $item->plan_id,
                        'plan_name' => $item->plan?->name ?? 'Sin plan',
                        'plan_price' => $item->plan?->price ?? 0,
                        'badge_color' => $item->plan?->badge_color ?? 'gray',
                        'count' => $item->count,
                    ];
                })
                ->sortByDesc('count')
                ->values();
        }, 'daily');
    }

    /**
     * Métricas de ingresos (MRR/ARR).
     */
    public function getRevenueMetrics(): array
    {
        return $this->cache->remember(0, 'platform.revenue', function () {
            $mrr = $this->calculateMRR();
            $arr = $mrr * 12;

            // MRR del mes anterior para comparación
            $lastMonthMRR = $this->calculateMRR(now()->subMonth());
            $mrrGrowth = $lastMonthMRR > 0
                ? round((($mrr - $lastMonthMRR) / $lastMonthMRR) * 100, 1)
                : ($mrr > 0 ? 100 : 0);

            return [
                'mrr' => Money::of($mrr)->value(),
                'arr' => Money::of($arr)->value(),
                'mrr_growth_percentage' => $mrrGrowth,
                'last_month_mrr' => Money::of($lastMonthMRR)->value(),
            ];
        }, 'daily');
    }

    /**
     * Calcula el MRR (Monthly Recurring Revenue).
     *
     * MRR = Σ (subscription_price / billing_cycle_months)
     * Solo para suscripciones activas.
     */
    public function calculateMRR(?Carbon $asOf = null): float
    {
        $asOf = $asOf ?? now();

        $subscriptions = Subscription::query()
            ->whereIn('status', [
                Subscription::STATUS_ACTIVE,
                Subscription::STATUS_TRIAL,
                Subscription::STATUS_GRACE,
            ])
            ->where('starts_at', '<=', $asOf)
            ->with('plan:id,price')
            ->get();

        $mrr = Money::zero();

        foreach ($subscriptions as $subscription) {
            $price = $subscription->amount_paid ?? $subscription->plan?->price ?? 0;
            $cycleMonths = self::BILLING_CYCLE_MONTHS[$subscription->billing_cycle] ?? 1;

            // Convertir a mensual
            $monthlyEquivalent = $price / $cycleMonths;
            $mrr = $mrr->add($monthlyEquivalent);
        }

        return $mrr->value();
    }

    /**
     * Métricas de crecimiento.
     */
    public function getGrowthMetrics(): array
    {
        return $this->cache->remember(0, 'platform.growth', function () {
            return [
                'churn_rate' => $this->calculateChurnRate(),
                'trial_conversion_rate' => $this->calculateTrialConversionRate(),
            ];
        }, 'daily');
    }

    /**
     * Calcula el Churn Rate mensual.
     *
     * Churn = (Cancelaciones del mes / Suscripciones activas inicio de mes) × 100
     */
    public function calculateChurnRate(int $months = 1): float
    {
        $startDate = now()->subMonths($months)->startOfMonth();
        $endDate = now()->subMonths($months - 1)->endOfMonth();

        // Suscripciones activas al inicio del período
        $activeAtStart = Subscription::query()
            ->where('starts_at', '<', $startDate)
            ->whereIn('status', [
                Subscription::STATUS_ACTIVE,
                Subscription::STATUS_TRIAL,
                Subscription::STATUS_GRACE,
                Subscription::STATUS_CANCELED, // Incluir para contar las que se cancelaron
                Subscription::STATUS_EXPIRED,
            ])
            ->where(function ($q) use ($startDate) {
                $q->whereNull('canceled_at')
                    ->orWhere('canceled_at', '>=', $startDate);
            })
            ->count();

        if ($activeAtStart === 0) {
            return 0;
        }

        // Cancelaciones durante el período
        $cancellations = Subscription::query()
            ->whereBetween('canceled_at', [$startDate, $endDate])
            ->count();

        return round(($cancellations / $activeAtStart) * 100, 2);
    }

    /**
     * Calcula la tasa de conversión de trials.
     *
     * Conversion = (Trials convertidos a paid / Trials iniciados) × 100
     * Últimos 30 días por defecto.
     */
    public function calculateTrialConversionRate(int $days = 30): float
    {
        $startDate = now()->subDays($days);

        // Trials que terminaron en el período
        $trialsEnded = Subscription::query()
            ->where('status', '!=', Subscription::STATUS_TRIAL)
            ->whereNotNull('trial_ends_at')
            ->where('trial_ends_at', '>=', $startDate)
            ->where('trial_ends_at', '<=', now())
            ->count();

        if ($trialsEnded === 0) {
            return 0;
        }

        // Trials que se convirtieron a activos
        $converted = Subscription::query()
            ->where('status', Subscription::STATUS_ACTIVE)
            ->whereNotNull('trial_ends_at')
            ->where('trial_ends_at', '>=', $startDate)
            ->where('trial_ends_at', '<=', now())
            ->count();

        return round(($converted / $trialsEnded) * 100, 2);
    }

    /**
     * Tendencia de MRR histórico.
     *
     * @return Collection<int, array{month: string, mrr: float}>
     */
    public function getMRRTrend(int $months = 12): Collection
    {
        return $this->cache->remember(0, "platform.mrr_trend_{$months}", function () use ($months) {
            $result = collect();

            for ($i = $months - 1; $i >= 0; $i--) {
                $date = now()->subMonths($i);
                $mrr = $this->calculateMRR($date->endOfMonth());

                $result->push([
                    'month' => $date->format('Y-m'),
                    'month_label' => $date->translatedFormat('M Y'),
                    'mrr' => $mrr,
                ]);
            }

            return $result;
        }, 'daily');
    }

    /**
     * Uso promedio de límites por plan.
     *
     * @return Collection<int, array{
     *   plan_id: int,
     *   plan_name: string,
     *   avg_users_usage: float,
     *   avg_credits_usage: float,
     *   avg_clients_usage: float,
     *   companies_count: int
     * }>
     */
    public function getAverageUsageByPlan(): Collection
    {
        return $this->cache->remember(0, 'platform.usage_by_plan', function () {
            $plans = Plan::active()->with(['subscriptions' => function ($q) {
                $q->whereIn('status', [
                    Subscription::STATUS_ACTIVE,
                    Subscription::STATUS_TRIAL,
                    Subscription::STATUS_GRACE,
                ])->with('company');
            }])->get();

            $result = collect();

            foreach ($plans as $plan) {
                $subscriptions = $plan->subscriptions;

                if ($subscriptions->isEmpty()) {
                    continue;
                }

                $usageData = [
                    'users' => [],
                    'credits' => [],
                    'clients' => [],
                ];

                foreach ($subscriptions as $subscription) {
                    $company = $subscription->company;
                    if (! $company) {
                        continue;
                    }

                    $stats = $company->getUsageStats();

                    if ($subscription->getMaxUsers() > 0) {
                        $usageData['users'][] = $stats['users_percentage'];
                    }
                    if ($subscription->getMaxActiveCredits() > 0) {
                        $usageData['credits'][] = $stats['credits_percentage'];
                    }
                    if ($subscription->getMaxClients() > 0) {
                        $usageData['clients'][] = $stats['clients_percentage'];
                    }
                }

                $result->push([
                    'plan_id' => $plan->id,
                    'plan_name' => $plan->name,
                    'avg_users_usage' => ! empty($usageData['users'])
                        ? round(array_sum($usageData['users']) / count($usageData['users']), 1)
                        : 0,
                    'avg_credits_usage' => ! empty($usageData['credits'])
                        ? round(array_sum($usageData['credits']) / count($usageData['credits']), 1)
                        : 0,
                    'avg_clients_usage' => ! empty($usageData['clients'])
                        ? round(array_sum($usageData['clients']) / count($usageData['clients']), 1)
                        : 0,
                    'companies_count' => $subscriptions->count(),
                ]);
            }

            return $result->sortByDesc('companies_count')->values();
        }, 'slow');
    }

    /**
     * Companies cerca del límite (>80% de uso).
     */
    public function getCompaniesNearLimit(): Collection
    {
        return $this->cache->remember(0, 'platform.near_limit', function () {
            $threshold = 80;

            return Company::where('status', 'active')
                ->get()
                ->filter(function ($company) use ($threshold) {
                    $stats = $company->getUsageStats();
                    $usersPercentage = $stats['users']['percentage'] ?? 0;
                    $creditsPercentage = $stats['credits']['percentage'] ?? 0;
                    $clientsPercentage = $stats['clients']['percentage'] ?? 0;

                    return $usersPercentage >= $threshold
                        || $creditsPercentage >= $threshold
                        || $clientsPercentage >= $threshold;
                })
                ->map(function ($company) {
                    $stats = $company->getUsageStats();
                    $subscription = $company->currentSubscription;

                    $usersPercentage = $stats['users']['percentage'] ?? 0;
                    $creditsPercentage = $stats['credits']['percentage'] ?? 0;
                    $clientsPercentage = $stats['clients']['percentage'] ?? 0;

                    return [
                        'company_id' => $company->id,
                        'company_name' => $company->name,
                        'plan_name' => $subscription?->plan?->name ?? 'N/A',
                        'users_usage' => $usersPercentage,
                        'credits_usage' => $creditsPercentage,
                        'clients_usage' => $clientsPercentage,
                        'max_usage' => max(
                            $usersPercentage,
                            $creditsPercentage,
                            $clientsPercentage
                        ),
                    ];
                })
                ->sortByDesc('max_usage')
                ->take(10)
                ->values();
        }, 'slow');
    }

    /**
     * Get status label.
     */
    private function getStatusLabel(string $status): string
    {
        return match ($status) {
            Subscription::STATUS_ACTIVE => 'Activo',
            Subscription::STATUS_TRIAL => 'Prueba',
            Subscription::STATUS_GRACE => 'Gracia',
            Subscription::STATUS_EXPIRED => 'Expirado',
            Subscription::STATUS_CANCELED => 'Cancelado',
            Subscription::STATUS_SUSPENDED => 'Suspendido',
            default => ucfirst($status),
        };
    }

    /**
     * Get status color for charts.
     */
    private function getStatusColor(string $status): string
    {
        return match ($status) {
            Subscription::STATUS_ACTIVE => '#10b981',    // green
            Subscription::STATUS_TRIAL => '#3b82f6',     // blue
            Subscription::STATUS_GRACE => '#f59e0b',     // amber
            Subscription::STATUS_EXPIRED => '#ef4444',   // red
            Subscription::STATUS_CANCELED => '#6b7280',  // gray
            Subscription::STATUS_SUSPENDED => '#dc2626', // red-dark
            default => '#9ca3af',
        };
    }
}
