<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class AuthzUiContractTest extends TestCase
{
    use ProjectFileAssertionSupport;

    public function testAdminTenantTemplatesUseServerCapabilities(): void
    {
        $indexTemplate = $this->readProjectFile('pages/admin/tenants/index(default).phtml');
        $this->assertStringNotContainsString("can('tenants.create')", $indexTemplate);
        $this->assertStringContainsString('$canCreateTenants', $indexTemplate);

        $createTemplate = $this->readProjectFile('pages/admin/tenants/create(default).phtml');
        $this->assertStringNotContainsString("can('custom_fields.manage')", $createTemplate);
        $this->assertStringNotContainsString("can('tenants.sso_manage')", $createTemplate);
        $this->assertStringContainsString('$canManageCustomFields', $createTemplate);
        $this->assertStringContainsString('$canManageSso', $createTemplate);
    }


    public function testAdminDepartmentsListTemplateUsesServerCapabilities(): void
    {
        $content = $this->readProjectFile('pages/admin/departments/index(default).phtml');
        $this->assertStringNotContainsString("can('departments.create')", $content);
        $this->assertStringContainsString('$canCreateDepartments', $content);
    }


    public function testAdminRoleTemplatesUseServerCapabilities(): void
    {
        $indexTemplate = $this->readProjectFile('pages/admin/roles/index(default).phtml');
        $this->assertStringNotContainsString("can('roles.create')", $indexTemplate);
        $this->assertStringContainsString('$canCreateRoles', $indexTemplate);

        $editTemplate = $this->readProjectFile('pages/admin/roles/edit(default).phtml');
        $this->assertStringNotContainsString("can('roles.update')", $editTemplate);
        $this->assertStringNotContainsString("can('roles.delete')", $editTemplate);
        $this->assertStringContainsString('$canUpdateRole', $editTemplate);
        $this->assertStringContainsString('$canDeleteRole', $editTemplate);
    }


    public function testAdminPermissionTemplatesUseServerCapabilities(): void
    {
        $indexTemplate = $this->readProjectFile('pages/admin/permissions/index(default).phtml');
        $this->assertStringNotContainsString("can('permissions.create')", $indexTemplate);
        $this->assertStringContainsString('$canCreatePermissions', $indexTemplate);

        $editTemplate = $this->readProjectFile('pages/admin/permissions/edit(default).phtml');
        $this->assertStringNotContainsString("can('permissions.update')", $editTemplate);
        $this->assertStringNotContainsString("can('permissions.delete')", $editTemplate);
        $this->assertStringContainsString('$canUpdatePermission', $editTemplate);
        $this->assertStringContainsString('$canDeletePermission', $editTemplate);
    }


    public function testAdminSettingsTemplateUsesServerCapabilities(): void
    {
        $templateContent = $this->readProjectFile('pages/admin/settings/index(default).phtml');
        $this->assertStringNotContainsString("can('settings.update')", $templateContent);
        $this->assertStringContainsString('$canUpdateSettings', $templateContent);
    }


    public function testAdminUserEditTemplateUsesServerCapabilities(): void
    {
        $content = $this->readProjectFile('pages/admin/users/edit(default).phtml');
        $this->assertStringNotContainsString("can('users.", $content);
        $this->assertStringNotContainsString("can('permissions.view')", $content);
        $this->assertStringContainsString('$canViewSecurityArtifacts', $content);
    }


    public function testHardCutTemplatesDoNotUseLegacyCanHelper(): void
    {
        $templates = [
            'templates/partials/app-main-aside.phtml',
            'templates/partials/app-main-aside-icon-bar.phtml',
            'pages/admin/api-audit/index(default).phtml',
            'pages/admin/system-audit/index(default).phtml',
            'pages/admin/import-audit/index(default).phtml',
            'pages/admin/scheduled-jobs/index(default).phtml',
            'pages/admin/stats/index(default).phtml',
            'pages/admin/user-lifecycle-audit/index(default).phtml',
            'pages/admin/user-lifecycle-audit/view(default).phtml',
            'pages/admin/users/index(default).phtml',
            'pages/admin/users/create(default).phtml',
            'pages/admin/tenants/edit(default).phtml',
            'pages/admin/departments/edit(default).phtml',
        ];

        foreach ($templates as $template) {
            $content = $this->readProjectFile($template);
            $this->assertStringNotContainsString("can('", $content, $template);
        }
    }


    public function testLayoutPartialsUseViewAuthLayoutCapabilities(): void
    {
        $defaultTemplate = $this->readProjectFile('templates/default.phtml');
        $this->assertStringContainsString("\$viewAuth['layout']", $defaultTemplate);
        $this->assertStringNotContainsString('UiAccessService::class', $defaultTemplate);

        $aside = $this->readProjectFile('templates/partials/app-main-aside.phtml');
        $this->assertStringContainsString("\$viewAuth['layout']", $aside);
        $this->assertStringContainsString('layoutHasAdminPanel(', $aside);
        $this->assertStringContainsString('$layoutNav', $aside);

        $iconBar = $this->readProjectFile('templates/partials/app-main-aside-icon-bar.phtml');
        $this->assertStringContainsString("\$viewAuth['layout']", $iconBar);
        $this->assertStringContainsString('layoutHasAdminPanel(', $iconBar);
    }

    public function testAsideTemplateDoesNotReadRequestOrResolveServicesDirectly(): void
    {
        $aside = $this->readProjectFile('templates/partials/app-main-aside.phtml');

        $this->assertStringNotContainsString('$_GET', $aside);
        $this->assertStringNotContainsString('$_SESSION', $aside);
        $this->assertStringNotContainsString('TenantAvatarService', $aside);
        $this->assertStringNotContainsString('app(', $aside);
    }


    public function testHardCutActionsProvideViewAuthPageCapabilities(): void
    {
        $actions = [
            'pages/admin/users/index().php',
            'pages/admin/users/create().php',
            'pages/admin/tenants/edit($id).php',
            'pages/admin/departments/edit($id).php',
            'pages/admin/stats/index().php',
            'pages/admin/api-audit/index().php',
            'pages/admin/system-audit/index().php',
            'pages/admin/import-audit/index().php',
            'pages/admin/scheduled-jobs/index().php',
            'pages/admin/user-lifecycle-audit/index().php',
            'pages/admin/user-lifecycle-audit/view($id).php',
        ];

        foreach ($actions as $action) {
            $content = $this->readProjectFile($action);
            $this->assertStringContainsString("\$viewAuth['page']", $content, $action);
        }
    }

    #[DataProvider('adminPageAuthWiringProvider')]
    public function testAdminPageAuthKeysAreWiredFromActions(
        string $templateFile,
        string $actionFile,
        ?string $uiCapabilityMapConstant
    ): void {
        $templateContent = $this->readProjectFile($templateFile);
        $requiredKeys = $this->extractPageAuthKeys($templateContent);
        $this->assertNotEmpty($requiredKeys, $templateFile . ' must read at least one $pageAuth key.');

        $actionContent = $this->readProjectFile($actionFile);
        $this->assertStringContainsString("\$viewAuth['page']", $actionContent, $actionFile);

        $wiredKeys = $this->extractViewAuthPageKeys($actionContent);
        if ($uiCapabilityMapConstant !== null) {
            $this->assertStringContainsString('->pageCapabilities(', $actionContent, $actionFile);
            $this->assertStringContainsString($uiCapabilityMapConstant, $actionContent, $actionFile);
            $wiredKeys = array_values(array_unique([
                ...$wiredKeys,
                ...$this->extractUiCapabilityMapKeys($uiCapabilityMapConstant),
            ]));
        }

        $missingKeys = array_values(array_diff($requiredKeys, $wiredKeys));
        $this->assertSame(
            [],
            $missingKeys,
            sprintf(
                'Missing $pageAuth wiring in %s (from %s): %s',
                $templateFile,
                $actionFile,
                implode(', ', $missingKeys)
            )
        );
    }

    public function testTenantEditActionExposesDeleteCapabilityForTemplate(): void
    {
        $content = $this->readProjectFile('pages/admin/tenants/edit($id).php');
        $this->assertStringContainsString("'can_delete_tenant' =>", $content);
    }


    public function testMapDrivenHardCutActionsUseUiCapabilityMaps(): void
    {
        $actions = [
            'pages/admin/users/index().php' => 'UiCapabilityMap::PAGE_USERS_INDEX',
            'pages/admin/users/create().php' => 'UiCapabilityMap::PAGE_USERS_CREATE',
            'pages/admin/stats/index().php' => 'UiCapabilityMap::PAGE_STATS_INDEX',
            'pages/admin/api-audit/index().php' => 'UiCapabilityMap::PAGE_AUDIT_PURGE',
            'pages/admin/system-audit/index().php' => 'UiCapabilityMap::PAGE_SYSTEM_AUDIT',
            'pages/admin/import-audit/index().php' => 'UiCapabilityMap::PAGE_AUDIT_PURGE',
            'pages/admin/scheduled-jobs/index().php' => 'UiCapabilityMap::PAGE_AUDIT_PURGE',
            'pages/admin/user-lifecycle-audit/index().php' => 'UiCapabilityMap::PAGE_AUDIT_PURGE',
            'pages/admin/user-lifecycle-audit/view($id).php' => 'UiCapabilityMap::PAGE_USER_LIFECYCLE_VIEW',
        ];

        foreach ($actions as $action => $mapConstant) {
            $content = $this->readProjectFile($action);
            $this->assertStringContainsString('->pageCapabilities(', $content, $action);
            $this->assertStringContainsString($mapConstant, $content, $action);
        }
    }


    public function testUiAccessServiceUsesCentralLayoutMapDefinition(): void
    {
        $content = $this->readProjectFile('lib/Service/Access/UiAccessService.php');
        $this->assertStringContainsString('UiCapabilityMap::LAYOUT', $content);
    }

    public function testStatsPageCapabilityMapIncludesAuditCapabilities(): void
    {
        $content = $this->readProjectFile('lib/Service/Access/UiCapabilityMap.php');
        $this->assertStringContainsString("'can_view_system_audit' => OperationsAuthorizationPolicy::ABILITY_ADMIN_SYSTEM_AUDIT_VIEW", $content);
        $this->assertStringContainsString("'can_view_user_lifecycle_audit' => OperationsAuthorizationPolicy::ABILITY_ADMIN_USER_LIFECYCLE_AUDIT_VIEW", $content);
    }

    public function testStatsTemplateDefinesAuditTabAndPanel(): void
    {
        $content = $this->readProjectFile('pages/admin/stats/index(default).phtml');
        $this->assertStringContainsString('data-tab="audit"', $content);
        $this->assertStringContainsString('data-tab-panel="audit"', $content);
    }

    /**
     * @return array<string, array{0: string, 1: string, 2: string|null}>
     */
    public static function adminPageAuthWiringProvider(): array
    {
        return [
            'users index' => [
                'pages/admin/users/index(default).phtml',
                'pages/admin/users/index().php',
                'UiCapabilityMap::PAGE_USERS_INDEX',
            ],
            'users create' => [
                'pages/admin/users/create(default).phtml',
                'pages/admin/users/create().php',
                'UiCapabilityMap::PAGE_USERS_CREATE',
            ],
            'tenants edit' => [
                'pages/admin/tenants/edit(default).phtml',
                'pages/admin/tenants/edit($id).php',
                null,
            ],
            'departments edit' => [
                'pages/admin/departments/edit(default).phtml',
                'pages/admin/departments/edit($id).php',
                null,
            ],
            'stats index' => [
                'pages/admin/stats/index(default).phtml',
                'pages/admin/stats/index().php',
                'UiCapabilityMap::PAGE_STATS_INDEX',
            ],
            'api audit index' => [
                'pages/admin/api-audit/index(default).phtml',
                'pages/admin/api-audit/index().php',
                'UiCapabilityMap::PAGE_AUDIT_PURGE',
            ],
            'import audit index' => [
                'pages/admin/import-audit/index(default).phtml',
                'pages/admin/import-audit/index().php',
                'UiCapabilityMap::PAGE_AUDIT_PURGE',
            ],
            'scheduled jobs index' => [
                'pages/admin/scheduled-jobs/index(default).phtml',
                'pages/admin/scheduled-jobs/index().php',
                'UiCapabilityMap::PAGE_AUDIT_PURGE',
            ],
            'system audit index' => [
                'pages/admin/system-audit/index(default).phtml',
                'pages/admin/system-audit/index().php',
                'UiCapabilityMap::PAGE_SYSTEM_AUDIT',
            ],
            'user lifecycle index' => [
                'pages/admin/user-lifecycle-audit/index(default).phtml',
                'pages/admin/user-lifecycle-audit/index().php',
                'UiCapabilityMap::PAGE_AUDIT_PURGE',
            ],
            'user lifecycle view' => [
                'pages/admin/user-lifecycle-audit/view(default).phtml',
                'pages/admin/user-lifecycle-audit/view($id).php',
                'UiCapabilityMap::PAGE_USER_LIFECYCLE_VIEW',
            ],
        ];
    }

    /**
     * @return list<string>
     */
    private function extractPageAuthKeys(string $content): array
    {
        preg_match_all('/\\$pageAuth\\[[\'"]([a-z_]+)[\'"]\\]/', $content, $matches);
        $keys = array_values(array_unique($matches[1]));
        sort($keys);
        return $keys;
    }

    /**
     * @return list<string>
     */
    private function extractViewAuthPageKeys(string $content): array
    {
        $keys = [];
        preg_match_all('/\\$viewAuth\\[\'page\'\\]\\s*=\\s*\\[(.*?)\\];/s', $content, $blocks);
        foreach ($blocks[1] as $block) {
            preg_match_all('/[\'"]([a-z_]+)[\'"]\\s*=>/', $block, $matches);
            foreach ($matches[1] as $key) {
                $keys[] = $key;
            }
        }

        $keys = array_values(array_unique($keys));
        sort($keys);
        return $keys;
    }

    /**
     * @return list<string>
     */
    private function extractUiCapabilityMapKeys(string $mapConstant): array
    {
        $parts = explode('::', $mapConstant);
        $constantName = trim((string) end($parts));
        $this->assertNotSame('', $constantName, 'Invalid UiCapabilityMap constant: ' . $mapConstant);

        $mapContent = $this->readProjectFile('lib/Service/Access/UiCapabilityMap.php');
        $constantPattern = '/public const\\s+' . preg_quote($constantName, '/') . '\\s*=\\s*\\[(.*?)\\];/s';
        $matched = preg_match($constantPattern, $mapContent, $constantMatch);
        $this->assertSame(
            1,
            $matched,
            'Could not find UiCapabilityMap constant block: ' . $mapConstant
        );

        preg_match_all('/[\'"]([a-z_]+)[\'"]\\s*=>/', (string) ($constantMatch[1] ?? ''), $keyMatches);
        $keys = array_values(array_unique($keyMatches[1]));
        sort($keys);
        return $keys;
    }


}
