<?php

declare(strict_types=1);

namespace App\Services\Academy;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use RuntimeException;

final class AcademyBillingPlanService
{
    public function enabled(): bool
    {
        return (bool) config('academy_billing.enabled', false);
    }

    public function subscriptionName(): string
    {
        return (string) config('academy_billing.subscription_name', 'academy');
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    public function plans(): array
    {
        $plans = config('academy_billing.plans', []);

        return is_array($plans) ? $plans : [];
    }

    public function normalizePlanKey(?string $planKey): string
    {
        return Str::of((string) $planKey)
            ->trim()
            ->lower()
            ->replace('-', '_')
            ->value();
    }

    /**
     * @return array<string, mixed>|null
     */
    public function plan(?string $planKey): ?array
    {
        $normalized = $this->normalizePlanKey($planKey);

        if ($normalized === '') {
            return null;
        }

        $plan = Arr::get($this->plans(), $normalized);

        if (! is_array($plan)) {
            return null;
        }

        $plan['key'] = $normalized;
        $plan['tier'] = $this->normalizeTier((string) ($plan['tier'] ?? 'free'));
        $plan['interval'] = Str::lower(trim((string) ($plan['interval'] ?? 'monthly')));
        $plan['amount'] = trim((string) ($plan['amount'] ?? ''));
        $plan['currency'] = Str::upper(trim((string) ($plan['currency'] ?? config('cashier.currency', 'EUR'))));
        $plan['stripe_price_id'] = trim((string) ($plan['stripe_price_id'] ?? ''));
        $plan['configured'] = $plan['stripe_price_id'] !== '';
        $plan['price_id_valid'] = $this->isValidPriceId($plan['stripe_price_id']);
        $plan['price_display'] = $plan['amount'] !== '' ? $plan['amount'].' '.$plan['currency'] : null;

        return $plan;
    }

    /**
     * @return array<string, mixed>|null
     */
    public function planForPriceId(?string $priceId): ?array
    {
        $priceId = trim((string) $priceId);

        if ($priceId === '') {
            return null;
        }

        foreach (array_keys($this->plans()) as $planKey) {
            $plan = $this->plan((string) $planKey);

            if ($plan !== null && ($plan['stripe_price_id'] ?? null) === $priceId) {
                return $plan;
            }
        }

        return null;
    }

    /**
     * @return array<int, string>
     */
    public function missingPriceIds(?string $planKey = null): array
    {
        if ($planKey !== null) {
            $plan = $this->plan($planKey);

            return $plan !== null && ! ($plan['configured'] ?? false)
                ? [$this->normalizePlanKey($planKey)]
                : [];
        }

        return collect(array_keys($this->plans()))
            ->filter(fn (string $key): bool => ! ((bool) ($this->plan($key)['configured'] ?? false)))
            ->values()
            ->all();
    }

    public function assertConfigured(?string $planKey = null): void
    {
        if (app()->environment(['local', 'testing'])) {
            return;
        }

        $missingPlans = $this->missingPriceIds($planKey);

        if ($missingPlans === []) {
            return;
        }

        throw new RuntimeException('Academy billing price IDs are missing for: '.implode(', ', $missingPlans));
    }

    public function normalizeTier(string $tier): string
    {
        return match (Str::lower(trim($tier))) {
            'admin' => 'admin',
            'pro' => 'pro',
            'creator', 'premium' => 'creator',
            default => 'free',
        };
    }

    public function isValidPriceId(?string $priceId): bool
    {
        $priceId = trim((string) $priceId);

        if ($priceId === '') {
            return false;
        }

        return preg_match('/^price_[A-Za-z0-9]+$/', $priceId) === 1;
    }
}