<?php

declare(strict_types=1);

namespace Maestro\Core\Services;

use Illuminate\Support\Facades\Schema;
use Maestro\Core\Models\MaestroModule;
use Maestro\Core\Models\NavigationNode;

/**
 * Read-only probes for Superadmin installation checklist (no Artisan from HTTP).
 *
 * Status: ok satisfied, warn incomplete or informational, fail blocking misconfiguration.
 */
final class InstallationHealthService
{
    private const CUSTOMERS_SLUG = 'customers';

    private const HOURS_SLUG = 'hours';

    private const OFFERS_SLUG = 'offers';

    private const SEEDER_CMD = 'php artisan db:seed --class="Maestro\\Core\\Database\\Seeders\\ModuleAndNavigationSeeder"';

    /**
     * @return array{checked_at:string,overall:string,groups:list<array{id:string,title:string,steps:list<array<string,mixed>>}>}
     */
    public function report(): array
    {
        $stepsCore = $this->coreSteps();
        $stepsCustomers = $this->customersSteps();
        $stepsHours = $this->hoursSteps();

        $stepsOffers = $this->offersSteps();

        $groups = [
            [
                'id' => 'core',
                'title' => 'Database & CORE',
                'steps' => $stepsCore,
            ],
            [
                'id' => 'customers',
                'title' => 'Customers module',
                'steps' => $stepsCustomers,
            ],
            [
                'id' => self::HOURS_SLUG,
                'title' => 'Hour Registration module',
                'steps' => $stepsHours,
            ],
            [
                'id' => self::OFFERS_SLUG,
                'title' => 'Offers module',
                'steps' => $stepsOffers,
            ],
        ];

        return [
            'checked_at' => now()->toIso8601String(),
            'overall' => $this->rollupOverall($groups),
            'groups' => $groups,
        ];
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function coreSteps(): array
    {
        $steps = [];

        $hasMigrateTable = Schema::hasTable('migrations');

        $hasMaestroModules = Schema::hasTable('maestro_modules');
        $hasNavigationNodes = Schema::hasTable('maestro_navigation_nodes');

        $steps[] = $this->makeStep(
            'migrations-table',
            'Laravel migrations table',
            $hasMigrateTable
                ? ['ok', 'The `migrations` table exists.', []]
                : ['fail', 'Database may be unreachable or Laravel was never migrated.', ['php artisan migrate']],
        );

        $steps[] = $this->makeStep(
            'maestro-modules-table',
            'Maestro CORE module registry table',
            $hasMaestroModules
                ? ['ok', 'Table `maestro_modules` exists.', []]
                : ['fail', 'CORE package migrations are missing — feature toggles cannot work.', ['php artisan migrate']],
        );

        $steps[] = $this->makeStep(
            'navigation-nodes-table',
            'Navigation table',
            $hasNavigationNodes
                ? ['ok', 'Table `maestro_navigation_nodes` exists.', []]
                : ['fail', 'CORE navigation migrations are missing.', ['php artisan migrate']],
        );

        $rootExists = $hasNavigationNodes && NavigationNode::query()->where('key', 'root')->exists();

        $steps[] = $this->makeStep(
            'navigation-root',
            'Default navigation tree (root)',
            $rootExists
                ? ['ok', 'Navigation root row is present.', []]
                : [
                    $hasMigrateTable ? 'warn' : 'fail',
                    $hasMigrateTable
                        ? 'No root row yet. Menus may be created on first `/api/navigation` request, but seeding is recommended.'
                        : 'Run migrations first.',
                    $hasMigrateTable ? [self::SEEDER_CMD] : ['php artisan migrate'],
                ],
        );

        $moduleCount = $hasMaestroModules ? MaestroModule::query()->count() : 0;

        $steps[] = $this->makeStep(
            'maestro-modules-rows',
            'Registered feature modules',
            $moduleCount > 0
                ? ['ok', sprintf('Found %d module definition(s).', $moduleCount), []]
                : [
                    'warn',
                    'No rows in `maestro_modules`. Sidebar and `/api/user` module slugs usually need seeded definitions.',
                    $hasMigrateTable ? [self::SEEDER_CMD] : ['php artisan migrate'],
                ],
        );

        $writableBootstrap = is_writable(base_path('bootstrap/cache'));

        $steps[] = $this->makeStep(
            'writable-bootstrap-cache',
            'Writable bootstrap cache',
            $writableBootstrap
                ? ['ok', '`bootstrap/cache` is writable.', []]
                : [
                    'warn',
                    '`bootstrap/cache` is not writable — Laravel may fail to cache config or routes.',
                    [],
                ],
        );

        return $steps;
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function customersSteps(): array
    {
        $steps = [];

        $customersInConfig = config('core.modules.customers', true) === true;

        $hasMaestroModules = Schema::hasTable('maestro_modules');
        $hasCustomersTable = Schema::hasTable('customers');

        $customersModuleRow = $hasMaestroModules
            ? MaestroModule::query()->where('slug', self::CUSTOMERS_SLUG)->first()
            : null;

        if (! $customersInConfig) {
            $steps[] = $this->makeStep(
                'customers-config-off',
                'Customers module (config)',
                [
                    'ok',
                    '`core.modules.customers` is false — the Customers PHP module is not loaded for this deployment.',
                    [],
                ],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'customers-config-on',
            'Customers module (config)',
            [
                'ok',
                '`core.modules.customers` is enabled.',
                [],
            ],
        );

        if (! $hasCustomersTable) {
            $steps[] = $this->makeStep(
                'customers-schema',
                'Customers database tables',
                [
                    'fail',
                    '`customers` table is missing while the module is enabled in config.',
                    ['php artisan migrate'],
                ],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'customers-schema',
            'Customers database tables',
            ['ok', 'Core `customers` table exists.', []],
        );

        if ($customersModuleRow === null) {
            $steps[] = $this->makeStep(
                'customers-module-row',
                'Customers runtime toggle row',
                [
                    'warn',
                    '`maestro_modules` has no row for slug `customers`.',
                    [self::SEEDER_CMD],
                ],
            );
        } elseif (! $customersModuleRow->is_enabled) {
            $steps[] = $this->makeStep(
                'customers-runtime-off',
                'Customers enabled for users',
                [
                    'warn',
                    'The Customers module exists but `is_enabled` is false — normal users have no Customers access.',
                    [],
                ],
            );
        } else {
            $steps[] = $this->makeStep(
                'customers-runtime-on',
                'Customers enabled for users',
                ['ok', 'Customers row exists and is enabled for runtime.', []],
            );
        }

        return $steps;
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function hoursSteps(): array
    {
        $steps = [];

        $hoursInConfig = config('core.modules.hours', true) === true;

        if (! $hoursInConfig) {
            $steps[] = $this->makeStep(
                'hours-config-off',
                'Hour Registration module (config)',
                ['ok', '`core.modules.hours` is false — the Hour Registration PHP module is not loaded.', []],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'hours-config-on',
            'Hour Registration module (config)',
            ['ok', '`core.modules.hours` is enabled.', []],
        );

        $hasMaestroModules = Schema::hasTable('maestro_modules');
        $hoursModuleRow = $hasMaestroModules
            ? MaestroModule::query()->where('slug', self::HOURS_SLUG)->first()
            : null;

        $hasCats = Schema::hasTable('hour_internal_categories');

        if (! $hasCats) {
            $steps[] = $this->makeStep(
                'hours-schema',
                'Hour Registration database tables',
                [
                    'fail',
                    '`hour_internal_categories` (or related tables) missing while Hours is enabled.',
                    ['php artisan migrate'],
                ],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'hours-schema',
            'Hour Registration core tables',
            ['ok', 'Hour Registration lookup tables appear present.', []],
        );

        if ($hoursModuleRow === null) {
            $steps[] = $this->makeStep(
                'hours-module-row',
                '`maestro_modules` row for Hours',
                [
                    'warn',
                    '`maestro_modules` has no row for slug `hours`.',
                    [self::SEEDER_CMD],
                ],
            );
        } elseif (! $hoursModuleRow->is_enabled) {
            $steps[] = $this->makeStep(
                'hours-runtime-off',
                'Hour Registration runtime toggle',
                [
                    'warn',
                    '`hours` module exists but `is_enabled` is false.',
                    [],
                ],
            );
        } else {
            $steps[] = $this->makeStep(
                'hours-runtime-on',
                'Hour Registration runtime toggle',
                ['ok', 'Hours module row exists and is enabled.', []],
            );
        }

        return $steps;
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function offersSteps(): array
    {
        $steps = [];

        $offersInConfig = config('core.modules.offers', true) === true;

        if (! $offersInConfig) {
            $steps[] = $this->makeStep(
                'offers-config-off',
                'Offers module (config)',
                ['ok', '`core.modules.offers` is false — the Offers PHP module is not loaded for this deployment.', []],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'offers-config-on',
            'Offers module (config)',
            ['ok', '`core.modules.offers` is enabled.', []],
        );

        $hasMaestroModules = Schema::hasTable('maestro_modules');

        /** @phpstan-ignore-next-line */
        $offersModuleRow = $hasMaestroModules
            ? MaestroModule::query()->where('slug', self::OFFERS_SLUG)->first()
            : null;

        $hasOffersTable = Schema::hasTable('offers');

        if (! $hasOffersTable) {
            $steps[] = $this->makeStep(
                'offers-schema',
                'Offers database tables',
                [
                    'fail',
                    '`offers` table is missing while the module is enabled in config.',
                    ['php artisan migrate'],
                ],
            );

            return $steps;
        }

        $steps[] = $this->makeStep(
            'offers-schema',
            'Offers database tables',
            ['ok', 'Core `offers` table exists.', []],
        );

        if ($offersModuleRow === null) {
            $steps[] = $this->makeStep(
                'offers-module-row',
                'Offers runtime toggle row',
                [
                    'warn',
                    '`maestro_modules` has no row for slug `offers`.',
                    [self::SEEDER_CMD],
                ],
            );
        } elseif (! $offersModuleRow->is_enabled) {
            $steps[] = $this->makeStep(
                'offers-runtime-off',
                'Offers enabled for users',
                [
                    'warn',
                    'The Offers module exists but `is_enabled` is false — normal users have no Offers access.',
                    [],
                ],
            );
        } else {
            $steps[] = $this->makeStep(
                'offers-runtime-on',
                'Offers enabled for users',
                ['ok', 'Offers row exists and is enabled for runtime.', []],
            );
        }

        return $steps;
    }

    /**
     * @param  array{0:'ok'|'warn'|'fail',1:string,2:list<string>}  $t
     * @return array<string, mixed>
     */
    private function makeStep(string $id, string $label, array $t): array
    {
        return [
            'id' => $id,
            'label' => $label,
            'status' => $t[0],
            'detail' => $t[1],
            'fix_commands' => $t[2],
        ];
    }

    /**
     * @param  list<array{steps:list<array{status:string}>}>  $groups
     */
    private function rollupOverall(array $groups): string
    {
        $worst = 'ok';
        foreach ($groups as $group) {
            foreach ($group['steps'] as $step) {
                $s = $step['status'];
                if ($s === 'fail') {
                    return 'fail';
                }
                if ($s === 'warn') {
                    $worst = 'warn';
                }
            }
        }

        return $worst;
    }
}
