<?php

declare(strict_types=1);

namespace Plugins\Larablog\Membership;

use App\Domain\Blog\AccessDecision;
use App\Domain\Blog\HtmlTeaser;
use App\Domain\Plugin\Hook;
use App\Domain\Plugin\PluginManager;
use App\Models\Article;
use App\Models\User;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Toggle;
use Filament\Panel;
use Filament\Schemas\Components\Section;
use Filament\Tables\Columns\IconColumn;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\ValidationException;
use Plugins\Larablog\Membership\Domain\MembershipService;
use Plugins\Larablog\Membership\Filament\Resources\MembershipPlanResource;
use Plugins\Larablog\Membership\Models\ArticleMembership;
use Plugins\Larablog\Membership\Models\MembershipPlan;
use Plugins\Larablog\Payment\Domain\ProductType;
use Plugins\Larablog\Payment\Models\Entitlement;
use Plugins\Larablog\Payment\Models\Order;

class PluginServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(MembershipService::class);

        Panel::configureUsing(function (Panel $panel): void {
            if ($panel->getId() !== 'admin') {
                return;
            }

            $panel->resources([MembershipPlanResource::class]);
        });
    }

    public function boot(): void
    {
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
        $this->loadViewsFrom(__DIR__.'/../resources/views', 'membership');

        $this->registerWebRoutes();
        $this->registerFilamentHooks();
        $this->registerAccessHook();
        $this->registerOrderPaidHook();
    }

    protected function registerWebRoutes(): void
    {
        Route::middleware('web')->prefix('plugins/membership')->group(function (): void {
            Route::get('/', function () {
                $plans = MembershipPlan::query()
                    ->enabled()
                    ->orderBy('sort_order')
                    ->orderBy('id')
                    ->get();

                return view('membership::plans', [
                    'plans' => $plans,
                    'status' => app(MembershipService::class)->statusFor(Auth::user()),
                ]);
            })->name('plugins.membership.plans');

            Route::get('/status', function () {
                $user = Auth::user();
                $status = app(MembershipService::class)->statusFor($user);

                return response()->json([
                    'ok' => true,
                    'plugin' => 'larablog/membership',
                    'authenticated' => $user !== null,
                    'roles' => $user?->getRoleNames() ?? [],
                    ...$status,
                ]);
            })->name('plugins.membership.status');
        });
    }

    protected function registerFilamentHooks(): void
    {
        Hook::listen('filament.article.form', function (array $components): array {
            if (! app(PluginManager::class)->isEnabled('larablog/membership')) {
                return [];
            }

            return [
                Section::make(__('admin.settings.membership'))
                    ->schema([
                        Toggle::make('membership.enabled')
                            ->label(__('admin.fields.membership_enabled'))
                            ->default(false),
                        Select::make('membership.required_plan_id')
                            ->label(__('admin.fields.required_plan'))
                            ->options(
                                MembershipPlan::query()
                                    ->orderBy('sort_order')
                                    ->pluck('name', 'id')
                                    ->all()
                            )
                            ->placeholder(__('admin.options.any_membership'))
                            ->nullable(),
                    ])
                    ->collapsible(),
            ];
        });

        Hook::listen('filament.article.mutate_before_fill', function (array $data, mixed $record): array {
            if (! $record instanceof Article) {
                return $data;
            }

            $row = ArticleMembership::query()->where('article_id', $record->id)->first();
            $data['membership'] = [
                'enabled' => (bool) ($row?->enabled ?? false),
                'required_plan_id' => $row?->required_plan_id,
            ];

            return $data;
        });

        Hook::listen('filament.article.validate_access_restrictions', function (array $data, mixed $record): array {
            $membership = is_array($data['membership'] ?? null) ? $data['membership'] : [];
            $membershipEnabled = (bool) ($membership['enabled'] ?? false);
            $paid = is_array($data['paid_content'] ?? null) ? $data['paid_content'] : [];
            $paidEnabled = (bool) ($paid['enabled'] ?? false);
            $hasPassword = filled($data['read_password'] ?? null);

            $flags = array_filter([
                'password' => $hasPassword,
                'paid' => $paidEnabled,
                'membership' => $membershipEnabled,
            ]);

            if (count($flags) > 1) {
                throw ValidationException::withMessages([
                    'read_password' => __('admin.messages.access_restriction_mutex'),
                    'paid_content.enabled' => __('admin.messages.access_restriction_mutex'),
                    'membership.enabled' => __('admin.messages.access_restriction_mutex'),
                ]);
            }

            return $data;
        });

        Hook::listen('filament.article.after_save', function (Article $record, array $data): void {
            $membership = $data['membership'] ?? null;
            if (! is_array($membership)) {
                return;
            }

            ArticleMembership::query()->updateOrCreate(
                ['article_id' => $record->id],
                [
                    'enabled' => (bool) ($membership['enabled'] ?? false),
                    'required_plan_id' => filled($membership['required_plan_id'] ?? null)
                        ? (int) $membership['required_plan_id']
                        : null,
                ],
            );
        });

        Hook::listen('filament.article.table.columns', function (array $columns): array {
            return [
                IconColumn::make('membership_enabled')
                    ->label(__('admin.fields.membership_enabled'))
                    ->boolean()
                    ->getStateUsing(function (Article $record): bool {
                        return ArticleMembership::query()
                            ->where('article_id', $record->id)
                            ->where('enabled', true)
                            ->exists();
                    }),
            ];
        });
    }

    protected function registerAccessHook(): void
    {
        Hook::listen('article.access', function (AccessDecision $decision, array $context): AccessDecision {
            if (! app(PluginManager::class)->isEnabled('larablog/membership')) {
                return $decision;
            }

            $article = $context['article'] ?? null;
            $user = $context['user'] ?? null;

            if (! $article instanceof Article) {
                return $decision;
            }

            $gate = ArticleMembership::query()
                ->where('article_id', $article->id)
                ->where('enabled', true)
                ->first();

            if ($gate === null) {
                return $decision;
            }

            $service = app(MembershipService::class);
            $allowed = $gate->required_plan_id
                ? $service->hasPlan($user instanceof User ? $user : null, (int) $gate->required_plan_id)
                : $service->isActive($user instanceof User ? $user : null);

            if ($allowed) {
                return $decision;
            }

            $teaser = app(HtmlTeaser::class)->truncate($article->renderedHtml(), 200);
            $checkoutUrl = $this->checkoutUrlForGate($gate, $article);

            return $decision->tightenWith(AccessDecision::needPurchase(
                $teaser,
                $checkoutUrl,
                __('frontend.article.membership_required'),
            ));
        });
    }

    protected function registerOrderPaidHook(): void
    {
        Hook::listen('order.paid', function (Order $order): void {
            $order->loadMissing('items');

            foreach ($order->items as $item) {
                if ($item->product_type !== ProductType::MEMBERSHIP) {
                    continue;
                }

                $plan = MembershipPlan::query()->find($item->product_id);
                $entitlement = Entitlement::query()
                    ->where('user_id', $order->user_id)
                    ->where('product_type', ProductType::MEMBERSHIP)
                    ->where('product_id', $item->product_id)
                    ->first();

                if ($entitlement === null) {
                    continue;
                }

                if ($plan === null) {
                    // Fail-closed: never turn a missing plan into a lifetime grant.
                    $entitlement->forceFill([
                        'revoked_at' => now(),
                        'expires_at' => null,
                    ])->save();

                    Log::warning('Membership order.paid revoked: plan missing.', [
                        'order_id' => $order->id,
                        'plan_id' => $item->product_id,
                    ]);

                    continue;
                }

                $entitlement->forceFill([
                    'expires_at' => $plan->duration_days !== null
                        ? now()->addDays((int) $plan->duration_days)
                        : null,
                    'revoked_at' => null,
                ])->save();
            }
        });
    }

    protected function checkoutUrlForGate(ArticleMembership $gate, Article $article): ?string
    {
        $plan = null;

        if ($gate->required_plan_id) {
            $plan = MembershipPlan::query()
                ->whereKey($gate->required_plan_id)
                ->where('enabled', true)
                ->first();
        }

        $plan ??= app(MembershipService::class)->cheapestEnabledPlan();

        if ($plan === null) {
            return null;
        }

        $query = http_build_query([
            'product_type' => ProductType::MEMBERSHIP,
            'product_id' => $plan->id,
            'return_url' => url('/show-'.$article->id.'.shtml'),
        ]);

        return url('/plugins/payment/checkout').'?'.$query;
    }
}
