<?php

declare(strict_types=1);

namespace Tests\Unit\Services;

use App\Models\UserInvitation;
use App\Services\UserService;
use Tests\TestCase;

class UserServiceTest extends TestCase
{
    private UserService $userService;
    private UserInvitation $invitationModel;
    private int $adminId;

    protected function setUp(): void
    {
        parent::setUp();
        $this->userService = new UserService();
        $this->invitationModel = new UserInvitation();
        $this->adminId = $this->createTestUser('admin@italvet.com', 'super_admin', 'active');
    }

    public function testGetUsers(): void
    {
        $this->createTestUser('user1@test.com', 'comercial_manager', 'active');
        $this->createTestUser('user2@test.com', 'comercial_manager', 'inactive');

        $result = $this->userService->getUsers();

        $this->assertArrayHasKey('items', $result);
        $this->assertArrayHasKey('total', $result);
        $this->assertGreaterThanOrEqual(3, $result['total']);
    }

    public function testGetUsersWithFilters(): void
    {
        $this->createTestUser('filter1@test.com', 'comercial_manager', 'active');
        $this->createTestUser('filter2@test.com', 'super_admin', 'active');

        $result = $this->userService->getUsers(['role' => 'super_admin']);
        $this->assertGreaterThanOrEqual(2, $result['total']);

        foreach ($result['items'] as $user) {
            $this->assertEquals('super_admin', $user['role']);
        }
    }

    public function testGetUsersWithSearch(): void
    {
        $this->createTestUser('searchable@test.com', 'comercial_manager', 'active');

        $result = $this->userService->getUsers(['search' => 'searchable']);
        $this->assertGreaterThanOrEqual(1, $result['total']);
    }

    public function testGetUsersPagination(): void
    {
        for ($i = 1; $i <= 5; $i++) {
            $this->createTestUser("page{$i}@test.com", 'comercial_manager', 'active');
        }

        $result = $this->userService->getUsers([], 1, 3);
        $this->assertCount(3, $result['items']);
        $this->assertEquals(3, $result['per_page']);
        $this->assertGreaterThan(3, $result['total']);
        $this->assertGreaterThan(1, $result['total_pages']);
    }

    public function testGetUser(): void
    {
        $id = $this->createTestUser('getuser@test.com', 'comercial_manager', 'active');
        $user = $this->userService->getUser($id);

        $this->assertEquals('getuser@test.com', $user['email']);
        $this->assertArrayNotHasKey('password', $user);
    }

    public function testGetUserNotFound(): void
    {
        $this->expectException(\RuntimeException::class);
        $this->userService->getUser(9999);
    }

    public function testCreateUser(): void
    {
        $user = $this->userService->createUser([
            'name' => 'Nuevo Gestor',
            'email' => 'nuevo@test.com',
            'role' => 'comercial_manager',
        ], $this->adminId);

        $this->assertEquals('nuevo@test.com', $user['email']);
        $this->assertEquals('comercial_manager', $user['role']);
        $this->assertEquals('pending_activation', $user['status']);
    }

    public function testCreateUserCreatesInvitation(): void
    {
        $this->userService->createUser([
            'name' => 'Invitado',
            'email' => 'invitado@test.com',
            'role' => 'comercial_manager',
        ], $this->adminId);

        $invitation = $this->invitationModel->findByEmail('invitado@test.com');
        $this->assertNotNull($invitation);
        $this->assertEquals('pendiente', $invitation['status']);
    }

    public function testCreateUserDuplicateEmail(): void
    {
        $this->userService->createUser([
            'name' => 'Original',
            'email' => 'duplicado@test.com',
            'role' => 'comercial_manager',
        ], $this->adminId);

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessage('ya está registrado');

        $this->userService->createUser([
            'name' => 'Duplicado',
            'email' => 'duplicado@test.com',
            'role' => 'comercial_manager',
        ], $this->adminId);
    }

    public function testUpdateUser(): void
    {
        $id = $this->createTestUser('update@test.com', 'comercial_manager', 'active');

        $user = $this->userService->updateUser($id, [
            'name' => 'Nombre Actualizado',
            'role' => 'super_admin',
        ]);

        $this->assertEquals('Nombre Actualizado', $user['name']);
        $this->assertEquals('super_admin', $user['role']);
    }

    public function testUpdateUserEmailConflict(): void
    {
        $this->createTestUser('existing@test.com', 'comercial_manager', 'active');
        $id2 = $this->createTestUser('other@test.com', 'comercial_manager', 'active');

        $this->expectException(\RuntimeException::class);
        $this->userService->updateUser($id2, ['email' => 'existing@test.com']);
    }

    public function testToggleStatus(): void
    {
        $id = $this->createTestUser('toggle@test.com', 'comercial_manager', 'active');

        $user = $this->userService->toggleStatus($id);
        $this->assertEquals('inactive', $user['status']);

        $user = $this->userService->toggleStatus($id);
        $this->assertEquals('active', $user['status']);
    }

    public function testResendInvitation(): void
    {
        $user = $this->userService->createUser([
            'name' => 'Reenviar',
            'email' => 'reenviar@test.com',
            'role' => 'comercial_manager',
        ], $this->adminId);

        $firstInvitation = $this->invitationModel->findByEmail('reenviar@test.com');

        $this->userService->resendInvitation($user['id'], $this->adminId);

        $firstAfter = $this->invitationModel->findByToken($firstInvitation['token']);
        $this->assertNull($firstAfter);

        $newInvitation = $this->invitationModel->findByEmail('reenviar@test.com');
        $this->assertNotNull($newInvitation);
        $this->assertNotEquals($firstInvitation['token'], $newInvitation['token']);
    }

    public function testResendInvitationAlreadyActive(): void
    {
        $id = $this->createTestUser('activo@test.com', 'comercial_manager', 'active');

        $this->expectException(\RuntimeException::class);
        $this->userService->resendInvitation($id, $this->adminId);
    }
}
