<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\Services\Academy\AcademyBillingPlanService;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;

final class AcademyBillingHealthCommand extends Command
{
    protected $signature = 'academy:billing-health
        {--json : Output machine-readable JSON}
        {--strict : Exit non-zero when blocking issues are found}';

    protected $description = 'Inspect Academy Stripe billing deployment readiness, config completeness, and Cashier route wiring';

    public function __construct(
        private readonly AcademyBillingPlanService $plans,
    ) {
        parent::__construct();
    }

    public function handle(): int
    {
        $report = $this->buildReport();

        if ((bool) $this->option('json')) {
            $this->line((string) json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));

            return $this->exitCodeFor($report);
        }

        $this->line('Academy Billing Health Check');
        $this->line('============================');
        $this->newLine();
        $this->line(sprintf('Environment: %s', $report['environment']));
        $this->line(sprintf('App URL: %s', $report['app_url'] ?? 'unset'));
        $this->line(sprintf('Academy enabled: %s', $report['academy_enabled'] ? 'yes' : 'no'));
        $this->line(sprintf('Academy billing enabled: %s', $report['academy_billing_enabled'] ? 'yes' : 'no'));
        $this->line(sprintf('Subscription name: %s', $report['subscription_name']));
        $this->line(sprintf('Cashier path: %s', $report['cashier_path']));
        $this->line(sprintf('Cashier webhook route: %s', $report['routes']['cashier_webhook']['present'] ? ($report['routes']['cashier_webhook']['url'] ?? 'present') : 'missing'));
        $this->line(sprintf('Academy pricing route: %s', $report['routes']['academy_pricing']['present'] ? ($report['routes']['academy_pricing']['url'] ?? 'present') : 'missing'));
        $this->line(sprintf('Academy billing account route: %s', $report['routes']['academy_billing_account']['present'] ? ($report['routes']['academy_billing_account']['url'] ?? 'present') : 'missing'));
        $this->line(sprintf('Stripe key configured: %s', $report['stripe']['publishable_key_configured'] ? 'yes' : 'no'));
        $this->line(sprintf('Stripe secret configured: %s', $report['stripe']['secret_key_configured'] ? 'yes' : 'no'));
        $this->line(sprintf('Webhook secret configured: %s', $report['stripe']['webhook_secret_configured'] ? 'yes' : 'no'));
        $this->line(sprintf('Cashier currency: %s', $report['stripe']['currency'] ?: 'unset'));
        $this->line(sprintf('Cashier locale: %s', $report['stripe']['currency_locale'] ?: 'unset'));
        $this->line(sprintf('Configured plans: %d', $report['configured_plan_count']));
        $this->line(sprintf('Plans missing Stripe price IDs: %d', count($report['missing_plan_keys'])));
        $this->line(sprintf('Billing tables present: %s', $report['tables']['subscriptions'] && $report['tables']['subscription_items'] && $report['tables']['academy_billing_events'] ? 'yes' : 'no'));
        $this->line(sprintf('User billing columns present: %s', $report['users_billing_columns_present'] ? 'yes' : 'no'));
        $this->newLine();

        foreach ($report['blockers'] as $blocker) {
            $this->error(sprintf('BLOCKER: %s', $blocker));
        }

        foreach ($report['warnings'] as $warning) {
            $this->warn(sprintf('WARNING: %s', $warning));
        }

        if ($report['plan_summaries'] !== []) {
            $this->newLine();
            $this->line('Plans');
            $this->line('-----');

            foreach ($report['plan_summaries'] as $plan) {
                $this->line(sprintf(
                    '%s: tier=%s interval=%s price_id=%s',
                    $plan['key'],
                    $plan['tier'],
                    $plan['interval'],
                    $plan['configured'] ? 'configured' : 'missing'
                ));
            }
        }

        $this->newLine();
        $this->info(sprintf('Status: %s', $report['status']));

        return $this->exitCodeFor($report);
    }

    /**
     * @return array<string, mixed>
     */
    private function buildReport(): array
    {
        $stripeKey = (string) config('cashier.key', '');
        $stripeSecret = (string) config('cashier.secret', env('STRIPE_SECRET', ''));
        $webhookSecret = (string) config('cashier.webhook.secret', env('STRIPE_WEBHOOK_SECRET', ''));
        $currency = trim((string) config('cashier.currency', env('CASHIER_CURRENCY', '')));
        $currencyLocale = trim((string) config('cashier.currency_locale', env('CASHIER_CURRENCY_LOCALE', '')));
        $academyEnabled = (bool) config('academy.enabled', true);
        $billingEnabled = $this->plans->enabled();
        $missingPlanKeys = $this->plans->missingPriceIds();
        $routes = [
            'cashier_webhook' => $this->routeStatus('cashier.webhook'),
            'academy_pricing' => $this->routeStatus('academy.pricing'),
            'academy_billing_account' => $this->routeStatus('academy.billing.account'),
            'academy_billing_portal' => $this->routeStatus('academy.billing.portal'),
            'admin_academy_billing' => $this->routeStatus('admin.academy.billing'),
        ];
        $tables = [
            'users' => Schema::hasTable('users'),
            'subscriptions' => Schema::hasTable('subscriptions'),
            'subscription_items' => Schema::hasTable('subscription_items'),
            'academy_billing_events' => Schema::hasTable('academy_billing_events'),
        ];
        $userBillingColumns = [
            'stripe_id' => $tables['users'] && Schema::hasColumn('users', 'stripe_id'),
            'pm_type' => $tables['users'] && Schema::hasColumn('users', 'pm_type'),
            'pm_last_four' => $tables['users'] && Schema::hasColumn('users', 'pm_last_four'),
            'trial_ends_at' => $tables['users'] && Schema::hasColumn('users', 'trial_ends_at'),
        ];
        $planSummaries = collect(array_keys($this->plans->plans()))
            ->map(function (string $key): array {
                $plan = $this->plans->plan($key);

                return [
                    'key' => $key,
                    'tier' => (string) ($plan['tier'] ?? 'free'),
                    'interval' => (string) ($plan['interval'] ?? 'monthly'),
                    'configured' => (bool) ($plan['configured'] ?? false),
                ];
            })
            ->values()
            ->all();

        $blockers = [];
        $warnings = [];

        if (! $academyEnabled) {
            $warnings[] = 'SKINBASE_ACADEMY_ENABLED is disabled, so billing cannot be reached by users.';
        }

        if (! $billingEnabled) {
            $warnings[] = 'ACADEMY_BILLING_ENABLED is disabled. Checkout routes will stay unavailable until rollout is enabled.';
        }

        if (! $this->isConfiguredSecret($stripeKey, 'pk_')) {
            $blockers[] = 'STRIPE_KEY is missing or still using a placeholder value.';
        }

        if (! $this->isConfiguredSecret($stripeSecret, 'sk_')) {
            $blockers[] = 'STRIPE_SECRET is missing or still using a placeholder value.';
        }

        if (! $this->isConfiguredSecret($webhookSecret, 'whsec_')) {
            $blockers[] = 'STRIPE_WEBHOOK_SECRET is missing or still using a placeholder value.';
        }

        if ($currency === '') {
            $blockers[] = 'CASHIER_CURRENCY is not configured.';
        }

        if ($currencyLocale === '') {
            $warnings[] = 'CASHIER_CURRENCY_LOCALE is not configured.';
        }

        if ($missingPlanKeys !== []) {
            $blockers[] = 'Stripe price IDs are missing for: '.implode(', ', $missingPlanKeys).'.';
        }

        if (! $routes['cashier_webhook']['present']) {
            $blockers[] = 'Cashier webhook route is missing; Stripe cannot sync subscriptions.';
        }

        if (! $routes['academy_pricing']['present']) {
            $blockers[] = 'Academy pricing route is missing.';
        }

        if (! $routes['academy_billing_account']['present']) {
            $blockers[] = 'Academy billing account route is missing.';
        }

        foreach ($tables as $table => $present) {
            if (! $present) {
                $blockers[] = sprintf('Required billing table %s is missing.', $table);
            }
        }

        foreach ($userBillingColumns as $column => $present) {
            if (! $present) {
                $blockers[] = sprintf('Required users.%s billing column is missing.', $column);
            }
        }

        if (! $routes['admin_academy_billing']['present']) {
            $warnings[] = 'Moderation Academy billing overview route is missing.';
        }

        if (Arr::where($planSummaries, fn (array $plan): bool => $plan['configured'] === false) === []) {
            $warnings[] = 'All configured Academy plans have Stripe price IDs. Verify they are live-mode IDs before production rollout.';
        }

        $invalidPlanKeys = collect(array_keys($this->plans->plans()))
            ->filter(function (string $key): bool {
                $plan = $this->plans->plan($key);

                return $plan !== null && ($plan['configured'] ?? false) && ! ($plan['price_id_valid'] ?? false);
            })
            ->values()
            ->all();

        if ($invalidPlanKeys !== []) {
            $blockers[] = 'Stripe price IDs are malformed for: '.implode(', ', $invalidPlanKeys).'. Use real price object IDs that start with price_.';
        }

        $status = $blockers !== []
            ? 'BLOCKED'
            : ($warnings !== [] ? 'WARNING' : 'OK');

        return [
            'environment' => app()->environment(),
            'app_url' => config('app.url'),
            'academy_enabled' => $academyEnabled,
            'academy_billing_enabled' => $billingEnabled,
            'subscription_name' => $this->plans->subscriptionName(),
            'cashier_path' => (string) config('cashier.path', 'stripe'),
            'stripe' => [
                'publishable_key_configured' => $this->isConfiguredSecret($stripeKey, 'pk_'),
                'secret_key_configured' => $this->isConfiguredSecret($stripeSecret, 'sk_'),
                'webhook_secret_configured' => $this->isConfiguredSecret($webhookSecret, 'whsec_'),
                'currency' => $currency,
                'currency_locale' => $currencyLocale,
            ],
            'configured_plan_count' => count($planSummaries),
            'missing_plan_keys' => $missingPlanKeys,
            'invalid_plan_keys' => $invalidPlanKeys,
            'plan_summaries' => $planSummaries,
            'routes' => $routes,
            'tables' => $tables,
            'user_billing_columns' => $userBillingColumns,
            'users_billing_columns_present' => ! in_array(false, $userBillingColumns, true),
            'blockers' => array_values(array_unique($blockers)),
            'warnings' => array_values(array_unique($warnings)),
            'status' => $status,
        ];
    }

    /**
     * @return array{present: bool, url: string|null}
     */
    private function routeStatus(string $name): array
    {
        if (! Route::has($name)) {
            return [
                'present' => false,
                'url' => null,
            ];
        }

        return [
            'present' => true,
            'url' => route($name),
        ];
    }

    private function isConfiguredSecret(string $value, string $expectedPrefix): bool
    {
        $value = trim($value);

        if ($value === '' || ! str_starts_with($value, $expectedPrefix)) {
            return false;
        }

        return ! str_contains(strtolower($value), 'xxx');
    }

    /**
     * @param array<string, mixed> $report
     */
    private function exitCodeFor(array $report): int
    {
        if ((bool) $this->option('strict') && $report['status'] === 'BLOCKED') {
            return self::FAILURE;
        }

        return self::SUCCESS;
    }
}