<?php

declare(strict_types=1);

namespace Maestro\Core\Services;

use Illuminate\Http\Request;
use Illuminate\Routing\Route as RoutingRoute;
use Illuminate\Support\Facades\Route;
use Maestro\Core\Http\Controllers\PublicHomeController;
use Maestro\Core\Models\MaestroAppSetting;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

final class PublicPageHealthService
{
    /**
     * @return array{
     *   route:string,
     *   status:'ok'|'warn'|'fail',
     *   status_detail:string,
     *   checks:list<array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}>
     * }
     */
    public function report(): array
    {
        $row = MaestroAppSetting::instance();
        $settings = is_array($row->public_page) ? $row->public_page : [];
        $enabled = (bool) ($settings['enabled'] ?? false);
        $mode = isset($settings['mode']) && is_string($settings['mode'])
            ? $settings['mode']
            : 'core_default';
        $rootMode = isset($settings['root_mode']) && is_string($settings['root_mode'])
            ? $settings['root_mode']
            : 'login';
        if (! in_array($rootMode, ['dashboard', 'login', 'public'], true)) {
            $rootMode = 'login';
        }

        $checks = [
            $this->rootRouteCheck($enabled, $rootMode),
            $this->namedRouteCheck($rootMode),
            $this->coreViewCheck(),
            $this->viteEntryCheck(),
            $this->customerOverrideCheck($mode),
        ];

        $status = $this->overallFromChecks($checks);
        $statusDetail = match ($status) {
            'ok' => 'Public homepage looks correctly wired.',
            'warn' => 'Public homepage is partially configured.',
            default => 'Public homepage is misconfigured for current settings.',
        };

        return [
            'route' => '/',
            'status' => $status,
            'status_detail' => $statusDetail,
            'checks' => $checks,
        ];
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function rootRouteCheck(bool $enabled, string $rootMode): array
    {
        try {
            $route = Route::getRoutes()->match(Request::create('/', 'GET'));
        } catch (NotFoundHttpException) {
            return $this->result(
                'root-route-explicit',
                'Root `/` route is registered',
                $enabled ? 'fail' : 'warn',
                'No GET route for `/` was found in the host application.',
                ['Run php artisan core:install --force to publish routes/public.php'],
            );
        }
        if (! $this->routeUsesPublicHomeController($route)) {
            return $this->result(
                'root-route-explicit',
                'Root `/` route is registered',
                $enabled ? 'fail' : 'warn',
                'Root URL is not handled by Maestro PublicHomeController.',
                ['Ensure routes/public.php is loaded before SPA catch-all in routes/web.php'],
            );
        }

        if ($rootMode === 'public' && ! $enabled) {
            return $this->result(
                'root-route-explicit',
                'Root `/` route is registered',
                'warn',
                'Root mode is `public`, but public homepage is disabled; users will be redirected to login.',
                [],
            );
        }

        return $this->result(
            'root-route-explicit',
            'Root `/` route is registered',
            'ok',
            sprintf('Root route resolves via PublicHomeController (mode: %s).', $rootMode),
            [],
        );
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function namedRouteCheck(string $rootMode): array
    {
        if (! Route::has('public.home')) {
            return $this->result(
                'public-home-name',
                'Named route `public.home`',
                'warn',
                'No named route `public.home` found.',
                ['Run php artisan core:install --force and verify routes/public.php is loaded'],
            );
        }

        $named = Route::getRoutes()->getByName('public.home');
        if ($named === null || $named->uri() !== '/') {
            return $this->result(
                'public-home-name',
                'Named route `public.home`',
                'warn',
                'Named route exists but does not point to `/`.',
                ['php artisan route:list | rg public.home'],
            );
        }

        return $this->result(
            'public-home-name',
            'Named route `public.home`',
            'ok',
            sprintf('Route `public.home` points to `/` (mode: %s).', $rootMode),
            [],
        );
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function coreViewCheck(): array
    {
        if (! view()->exists('maestro-core::public-home')) {
            return $this->result(
                'public-home-view',
                'Core Blade view `maestro-core::public-home`',
                'fail',
                'Package view for public homepage is missing.',
                [],
            );
        }

        return $this->result(
            'public-home-view',
            'Core Blade view `maestro-core::public-home`',
            'ok',
            'Core Blade view for public homepage is available.',
            [],
        );
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function viteEntryCheck(): array
    {
        if (! is_readable(resource_path('js/public-home.js'))) {
            return $this->result(
                'public-home-vite-entry',
                'Vite entry `resources/js/public-home.js`',
                'warn',
                'Public Vue entry file is missing.',
                ['Create resources/js/public-home.js and mount @maestro/core mountPublicHome()'],
            );
        }

        return $this->result(
            'public-home-vite-entry',
            'Vite entry `resources/js/public-home.js`',
            'ok',
            'Public Vue entry file is present.',
            [],
        );
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function customerOverrideCheck(string $mode): array
    {
        if ($mode !== 'customer_override') {
            return $this->result(
                'public-home-override',
                'Customer override component',
                'ok',
                'Mode is `core_default`; customer override is optional.',
                [],
            );
        }

        $overridePath = is_readable(resource_path('js/maestro/publicHome.vue'))
            ? 'resources/js/maestro/publicHome.vue'
            : (is_readable(resource_path('js/public/PublicHome.vue'))
                ? 'resources/js/public/PublicHome.vue'
                : null);

        if ($overridePath === null) {
            return $this->result(
                'public-home-override',
                'Customer override component',
                'fail',
                'Mode is `customer_override` but the host override Vue file is missing.',
                ['Create resources/js/maestro/publicHome.vue (or legacy resources/js/public/PublicHome.vue)'],
            );
        }

        return $this->result(
            'public-home-override',
            'Customer override component',
            'ok',
            'Customer override component is present.',
            [],
        );
    }

    /**
     * @param  list<array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}>  $checks
     * @return 'ok'|'warn'|'fail'
     */
    private function overallFromChecks(array $checks): string
    {
        $status = 'ok';
        foreach ($checks as $check) {
            if ($check['status'] === 'fail') {
                return 'fail';
            }
            if ($check['status'] === 'warn') {
                $status = 'warn';
            }
        }

        return $status;
    }

    /**
     * @return array{id:string,label:string,status:'ok'|'warn'|'fail',detail:string,commands:list<string>}
     */
    private function result(
        string $id,
        string $label,
        string $status,
        string $detail,
        array $commands,
    ): array {
        return [
            'id' => $id,
            'label' => $label,
            'status' => $status,
            'detail' => $detail,
            'commands' => $commands,
        ];
    }

    private function routeUsesPublicHomeController(RoutingRoute $route): bool
    {
        $action = $route->getAction('controller');
        if (! is_string($action)) {
            return false;
        }

        return str_starts_with($action, PublicHomeController::class.'@')
            || $action === PublicHomeController::class
            || str_contains($action, PublicHomeController::class);
    }
}
