<?php

declare(strict_types=1);

namespace Plugins\Larablog\Payment;

use App\Models\Article;
use App\Models\User;
use Filament\Panel;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Plugins\Larablog\Payment\Domain\OrderService;
use Plugins\Larablog\Payment\Domain\ProductType;
use Plugins\Larablog\Payment\Filament\Pages\PaymentSettingsPage;
use Plugins\Larablog\Payment\Filament\Resources\OrderResource;
use Plugins\Larablog\Payment\Models\Order;
use RuntimeException;
use Throwable;

class PluginServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Must run in register() so Panel::configureUsing is in place before
        // Filament builds the admin panel during package boot.
        Panel::configureUsing(function (Panel $panel): void {
            if ($panel->getId() !== 'admin') {
                return;
            }

            $panel
                ->resources([OrderResource::class])
                ->pages([PaymentSettingsPage::class]);
        });
    }

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

        Route::middleware(['web', 'auth'])
            ->prefix('plugins/payment')
            ->group(function (): void {
                Route::get('/checkout', function (Request $request, OrderService $orders) {
                    $returnUrl = self::safeReturnUrl($request->query('return_url'));

                    $productType = (string) $request->query('product_type', '');
                    $productId = (int) $request->query('product_id', 0);

                    if ($productType === '' || $productId < 1) {
                        return response()->view('payment::checkout.error', [
                            'message' => __('payment.errors.invalid_checkout'),
                            'returnUrl' => $returnUrl,
                        ], 422);
                    }

                    try {
                        [$title, $amount, $currency] = self::resolveCheckoutProduct($productType, $productId, $request);
                    } catch (RuntimeException $e) {
                        return response()->view('payment::checkout.error', [
                            'message' => $e->getMessage(),
                            'returnUrl' => $returnUrl,
                        ], 422);
                    }

                    /** @var User $user */
                    $user = $request->user();

                    try {
                        $order = $orders->createOrder(
                            $user,
                            $productType,
                            $productId,
                            $title,
                            $amount,
                            $currency,
                            'stub',
                        );
                    } catch (RuntimeException $e) {
                        return response()->view('payment::checkout.error', [
                            'message' => $e->getMessage(),
                            'returnUrl' => $returnUrl,
                        ], 409);
                    } catch (Throwable $e) {
                        report($e);

                        return response()->view('payment::checkout.error', [
                            'message' => __('payment.errors.checkout_failed'),
                            'returnUrl' => $returnUrl,
                        ], 500);
                    }

                    return redirect()->to(
                        url('/plugins/payment/orders/'.$order->id).'?return_url='.urlencode($returnUrl)
                    );
                })->name('plugins.payment.checkout');

                Route::get('/orders/{order}', function (Request $request, Order $order) {
                    abort_unless((int) $order->user_id === (int) $request->user()?->id, 403);

                    $order->loadMissing('items');

                    return view('payment::checkout.confirm', [
                        'order' => $order,
                        'returnUrl' => self::safeReturnUrl($request->query('return_url')),
                    ]);
                })->name('plugins.payment.orders.show');

                Route::post('/orders/{order}/pay', function (Request $request, Order $order, OrderService $orders) {
                    abort_unless((int) $order->user_id === (int) $request->user()?->id, 403);

                    $returnUrl = self::safeReturnUrl($request->input('return_url'));

                    try {
                        $orders->markPaid($order, [
                            'source' => 'stub_checkout',
                            'user_id' => $request->user()?->id,
                        ]);
                    } catch (RuntimeException $e) {
                        return redirect()
                            ->to(url('/plugins/payment/orders/'.$order->id).'?return_url='.urlencode($returnUrl))
                            ->withErrors(['pay' => $e->getMessage()]);
                    }

                    return redirect()->to($returnUrl);
                })->name('plugins.payment.orders.pay');
            });
    }

    /**
     * @return array{0: string, 1: string, 2: string}
     */
    protected static function resolveCheckoutProduct(string $productType, int $productId, Request $request): array
    {
        if ($productType === ProductType::ARTICLE) {
            $productClass = 'Plugins\\Larablog\\PaidContent\\Models\\ArticleProduct';
            if (! class_exists($productClass) || ! Schema::hasTable('article_products')) {
                throw new RuntimeException(__('payment.errors.invalid_checkout'));
            }

            // Only publicly readable articles can be sold; drafts/hidden posts
            // must not be purchasable by guessing an id.
            $article = Article::query()
                ->visible()
                ->published()
                ->find($productId);

            $product = $productClass::query()
                ->where('article_id', $productId)
                ->where('enabled', true)
                ->first();

            if ($article === null || $product === null) {
                throw new RuntimeException(__('payment.errors.invalid_checkout'));
            }

            return [
                (string) $article->title,
                (string) $product->price,
                (string) ($product->currency ?: 'CNY'),
            ];
        }

        if ($productType === ProductType::MEMBERSHIP) {
            $planClass = 'Plugins\\Larablog\\Membership\\Models\\MembershipPlan';
            if (! class_exists($planClass) || ! Schema::hasTable('membership_plans')) {
                throw new RuntimeException(__('payment.errors.invalid_checkout'));
            }

            $plan = $planClass::query()
                ->whereKey($productId)
                ->where('enabled', true)
                ->first();

            if ($plan === null) {
                throw new RuntimeException(__('payment.errors.invalid_checkout'));
            }

            return [
                (string) $plan->name,
                (string) $plan->price,
                (string) ($plan->currency ?: 'CNY'),
            ];
        }

        // Never trust client-supplied title/amount for unknown product types.
        throw new RuntimeException(__('payment.errors.invalid_checkout'));
    }

    protected static function safeReturnUrl(mixed $value): string
    {
        $url = is_string($value) ? trim($value) : '';

        if ($url === '') {
            return url('/');
        }

        if (str_starts_with($url, '/') && ! str_starts_with($url, '//')) {
            return url($url);
        }

        $appUrl = rtrim((string) config('app.url'), '/');
        if ($appUrl !== '' && str_starts_with($url, $appUrl)) {
            return $url;
        }

        return url('/');
    }
}
