<?php

declare(strict_types=1);

namespace App\Framework\Console\Security\Commands;

use App\Framework\Console\Attributes\ConsoleCommand;
use App\Framework\Console\ExitCode;
use App\Framework\Console\Input\ConsoleInput;
use App\Framework\Console\Layout\ResponsiveOutput;
use App\Framework\Console\Output\ConsoleOutput;
use App\Framework\Console\Security\Attributes\RequiresPermission;
use App\Framework\Console\Security\Permission;
use App\Framework\Console\Security\PermissionCategory;
use App\Framework\Console\Security\SecurityService;

final readonly class SecurityCommand
{
    public function __construct(
        private SecurityService $securityService
    ) {
    }

    #[ConsoleCommand(
        name: 'security:whoami',
        description: 'Show current user and permissions'
    )]
    public function whoami(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $user = $this->securityService->getCurrentUser();
        $json = $input->hasOption('json');

        if ($json) {
            $output->writeLine(json_encode($user->toArray(), JSON_PRETTY_PRINT));

            return ExitCode::SUCCESS;
        }

        $responsiveOutput = ResponsiveOutput::create($output);
        $output->writeLine("<yellow>Current Console User</yellow>\n");

        // Show user info
        $userInfo = [
            'ID' => $user->id,
            'Name' => $user->name,
            'Authenticated' => $this->securityService->isAuthenticated() ? 'Yes' : 'No',
            'Admin' => $user->isAdmin() ? 'Yes' : 'No',
            'Roles' => implode(', ', $user->roles),
        ];

        $responsiveOutput->writeKeyValue($userInfo);
        $output->writeLine('');

        // Show permissions by category
        $permissionsByCategory = $this->securityService->getUserPermissionsByCategory();

        if (empty($permissionsByCategory)) {
            $output->writeLine('<red>No permissions assigned</red>');

            return ExitCode::SUCCESS;
        }

        $permissionData = [];
        foreach ($permissionsByCategory as $categoryName => $permissions) {
            $permissionData[$categoryName] = implode(', ', $permissions);
        }

        $responsiveOutput->writeKeyValue($permissionData, 'Permissions by Category:');

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'security:permissions',
        description: 'List all available permissions and categories'
    )]
    public function listPermissions(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $category = $input->getArgument('category');
        $json = $input->hasOption('json');

        if ($category) {
            return $this->showCategoryPermissions($category, $output, $json);
        }

        if ($json) {
            $data = [];
            foreach (PermissionCategory::cases() as $cat) {
                $data[$cat->value] = [
                    'description' => $cat->getDescription(),
                    'permissions' => array_map(function (Permission $p) {
                        return [
                            'name' => $p->value,
                            'description' => $p->getDescription(),
                        ];
                    }, $cat->getPermissions()),
                ];
            }
            $output->writeLine(json_encode($data, JSON_PRETTY_PRINT));

            return ExitCode::SUCCESS;
        }

        $output->writeLine("<yellow>Permission Categories and Permissions</yellow>\n");

        foreach (PermissionCategory::cases() as $category) {
            $output->writeLine("<green>{$category->value}</green> - {$category->getDescription()}");

            $permissions = $category->getPermissions();
            foreach ($permissions as $permission) {
                $output->writeLine("  • {$permission->value} - {$permission->getDescription()}");
            }
            $output->writeLine('');
        }

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'security:check',
        description: 'Check if current user has specific permission'
    )]
    public function checkPermission(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $permissionName = $input->getArgument('permission');
        if (! $permissionName) {
            $output->writeLine('<red>Permission name is required</red>');

            return ExitCode::INVALID_ARGUMENTS;
        }

        try {
            $permission = Permission::from($permissionName);
        } catch (\ValueError) {
            $output->writeLine("<red>Invalid permission: {$permissionName}</red>");

            return ExitCode::INVALID_ARGUMENTS;
        }

        $hasPermission = $this->securityService->checkPermission($permission);
        $user = $this->securityService->getCurrentUser();

        if ($input->hasOption('json')) {
            $output->writeLine(json_encode([
                'user' => $user->id,
                'permission' => $permission->value,
                'has_permission' => $hasPermission,
            ], JSON_PRETTY_PRINT));

            return ExitCode::SUCCESS;
        }

        $status = $hasPermission ? '<green>✓ GRANTED</green>' : '<red>✗ DENIED</red>';
        $output->writeLine("Permission check for '{$permission->value}': {$status}");

        if (! $hasPermission) {
            $output->writeLine("User '{$user->name}' does not have the required permission.");
        }

        return $hasPermission ? ExitCode::SUCCESS : ExitCode::PERMISSION_DENIED;
    }

    #[ConsoleCommand(
        name: 'security:test-admin',
        description: 'Test command that requires admin permissions'
    )]
    #[RequiresPermission(Permission::ADMIN)]
    public function testAdmin(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $output->writeLine('<green>✓ Admin access confirmed!</green>');
        $output->writeLine('This command is only accessible to users with admin permissions.');

        return ExitCode::SUCCESS;
    }

    #[ConsoleCommand(
        name: 'security:test-analytics',
        description: 'Test command that requires analytics permissions'
    )]
    #[RequiresPermission([Permission::ANALYTICS_READ, Permission::ANALYTICS_WRITE])]
    public function testAnalytics(ConsoleInput $input, ConsoleOutput $output): ExitCode
    {
        $output->writeLine('<green>✓ Analytics access confirmed!</green>');
        $output->writeLine('This command requires both analytics read and write permissions.');

        return ExitCode::SUCCESS;
    }

    private function showCategoryPermissions(string $categoryName, ConsoleOutput $output, bool $json): ExitCode
    {
        try {
            $category = PermissionCategory::from($categoryName);
        } catch (\ValueError) {
            $output->writeLine("<red>Invalid category: {$categoryName}</red>");

            return ExitCode::INVALID_ARGUMENTS;
        }

        $permissions = $category->getPermissions();

        if ($json) {
            $data = [
                'category' => $category->value,
                'description' => $category->getDescription(),
                'permissions' => array_map(function (Permission $p) {
                    return [
                        'name' => $p->value,
                        'description' => $p->getDescription(),
                    ];
                }, $permissions),
            ];
            $output->writeLine(json_encode($data, JSON_PRETTY_PRINT));

            return ExitCode::SUCCESS;
        }

        $output->writeLine("<yellow>Category: {$category->value}</yellow>");
        $output->writeLine("Description: {$category->getDescription()}\n");

        $output->writeLine('<green>Permissions:</green>');
        foreach ($permissions as $permission) {
            $output->writeLine("  • {$permission->value} - {$permission->getDescription()}");
        }

        return ExitCode::SUCCESS;
    }
}
