<?php

/**
 * Service Provider для аутентифікації та авторизації
 *
 * @package Flowaxy\Core\System\Container\Providers
 * @version 1.0.0 Alpha prerelease
 */

declare(strict_types=1);

namespace Flowaxy\Core\System\Container\Providers;

use Flowaxy\Core\System\Container\ServiceProvider;
use Flowaxy\Contracts\Domain\User\AdminUserRepositoryInterface;
use Flowaxy\Contracts\Domain\User\AdminRoleRepositoryInterface;
use Flowaxy\Contracts\Domain\User\RoleRepositoryInterface;
use Flowaxy\Domain\User\Entities\Role;
use Flowaxy\Domain\User\Entities\AdminRole;
use Flowaxy\Infrastructure\Persistence\Repositories\AdminUserRepository;
use Flowaxy\Infrastructure\Persistence\Repositories\AdminRoleRepository;

final class AuthServiceProvider extends ServiceProvider
{
    protected function registerBindings(): void
    {
        if (! $this->container->has(AdminUserRepositoryInterface::class)) {
            $this->container->singleton(AdminUserRepositoryInterface::class, static fn() => new AdminUserRepository());
        }

        if (! $this->container->has(AdminRoleRepositoryInterface::class)) {
            $this->container->singleton(AdminRoleRepositoryInterface::class, static fn() => new AdminRoleRepository());
        }

        // Регистрируем адаптер для RoleRepositoryInterface
        // Адаптер использует AdminRoleRepository и конвертирует AdminRole в Role
        if (! $this->container->has(RoleRepositoryInterface::class)) {
            $this->container->singleton(RoleRepositoryInterface::class, function () {
                $adminRoleRepo = $this->container->make(AdminRoleRepositoryInterface::class);
                if ($adminRoleRepo instanceof \Closure) {
                    $adminRoleRepo = $adminRoleRepo();
                }
                return new class($adminRoleRepo) implements RoleRepositoryInterface {
                    public function __construct(private readonly AdminRoleRepositoryInterface $adminRoleRepo) {}

                    public function findById(int $id): ?Role
                    {
                        $adminRole = $this->adminRoleRepo->findById($id);
                        return $adminRole ? $this->convertToRole($adminRole) : null;
                    }

                    public function findByName(string $name): ?Role
                    {
                        $adminRole = $this->adminRoleRepo->findByName($name);
                        return $adminRole ? $this->convertToRole($adminRole) : null;
                    }

                    public function create(array $data): Role
                    {
                        $adminRole = $this->adminRoleRepo->create($data);
                        return $this->convertToRole($adminRole);
                    }

                    public function update(int $id, array $data): bool
                    {
                        return $this->adminRoleRepo->update($id, $data);
                    }

                    public function delete(int $id): bool
                    {
                        return $this->adminRoleRepo->delete($id);
                    }

                    public function findAll(): array
                    {
                        $adminRoles = $this->adminRoleRepo->findAll();
                        return array_map([$this, 'convertToRole'], $adminRoles);
                    }

                    public function getRolesForUser(int $userId): array
                    {
                        return $this->adminRoleRepo->getRolesForUser($userId);
                    }

                    public function getPermissionsForUser(int $userId): array
                    {
                        return $this->adminRoleRepo->getPermissionsForUser($userId);
                    }

                    public function userHasRole(int $userId, string $roleSlug): bool
                    {
                        return $this->adminRoleRepo->userHasRole($userId, $roleSlug);
                    }

                    public function userHasPermission(int $userId, string $permission): bool
                    {
                        return $this->adminRoleRepo->userHasPermission($userId, $permission);
                    }

                    public function assignRole(int $userId, int $roleId): bool
                    {
                        return $this->adminRoleRepo->assignRole($userId, $roleId);
                    }

                    public function removeRole(int $userId, int $roleId): bool
                    {
                        return $this->adminRoleRepo->removeRole($userId, $roleId);
                    }

                    private function convertToRole(AdminRole $adminRole): Role
                    {
                        return new Role(
                            id: $adminRole->id,
                            name: $adminRole->name,
                            slug: $adminRole->slug,
                            description: $adminRole->description,
                            isSystem: $adminRole->isSystem,
                            permissions: $adminRole->permissions
                        );
                    }
                };
            });
        }

        /** @var class-string $authServiceClass */
        $authServiceClass = 'Flowaxy\Domain\User\Services\AuthenticateAdminUserService';
        // @phpstan-ignore-next-line
        if (! $this->container->has($authServiceClass)) {
            $this->container->singleton($authServiceClass, function () use ($authServiceClass) {
                $userRepo = $this->container->make(AdminUserRepositoryInterface::class);
                // Якщо контейнер повернув Closure, викликаємо його
                if ($userRepo instanceof \Closure) {
                    $userRepo = $userRepo();
                }

                $roleRepo = null;
                if ($this->container->has(AdminRoleRepositoryInterface::class)) {
                    $roleRepo = $this->container->make(AdminRoleRepositoryInterface::class);
                    // Якщо контейнер повернув Closure, викликаємо його
                    if ($roleRepo instanceof \Closure) {
                        $roleRepo = $roleRepo();
                    }
                }

                /** @phpstan-ignore-next-line */
                return new $authServiceClass($userRepo, $roleRepo);
            });
        }

        /** @var class-string $authzServiceClass */
        $authzServiceClass = 'Flowaxy\Domain\User\Services\AuthorizationService';
        // @phpstan-ignore-next-line
        if (! $this->container->has($authzServiceClass)) {
            $this->container->singleton($authzServiceClass, function () use ($authzServiceClass) {
                // Используем RoleRepositoryInterface вместо AdminRoleRepositoryInterface
                $roleRepo = $this->container->make(RoleRepositoryInterface::class);
                // Якщо контейнер повернув Closure, викликаємо його
                if ($roleRepo instanceof \Closure) {
                    $roleRepo = $roleRepo();
                }
                /** @phpstan-ignore-next-line */
                return new $authzServiceClass($roleRepo);
            });
        }

        /** @var class-string $logoutServiceClass */
        $logoutServiceClass = 'Flowaxy\Domain\User\Services\LogoutUserService';
        // @phpstan-ignore-next-line
        if (! $this->container->has($logoutServiceClass)) {
            $this->container->singleton($logoutServiceClass, function () use ($logoutServiceClass) {
                $userRepo = $this->container->make(AdminUserRepositoryInterface::class);
                // Якщо контейнер повернув Closure, викликаємо його
                if ($userRepo instanceof \Closure) {
                    $userRepo = $userRepo();
                }
                /** @phpstan-ignore-next-line */
                return new $logoutServiceClass($userRepo);
            });
        }
    }
}
