<?php

namespace Tests\App\Controllers;

use App\Controllers\AuthController;
use App\Models\IpAttemptModel;
use App\Models\LoginActivityModel;
use App\Models\UserModel;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;

class FakeAuthJsonRequest extends MockIncomingRequest
{
    public function __construct(private readonly array $payload)
    {
        parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), 'php://input', new UserAgent());
    }

    public function getJSON(bool $assoc = false, int $depth = 512, int $options = 0): array
    {
        return $this->payload;
    }

    public function getPost($index = null, $filter = null, $flags = null)
    {
        if ($index === null) {
            return $this->payload;
        }

        return $this->payload[$index] ?? null;
    }
}

class TestableAuthController extends AuthController
{
    public function __construct()
    {
        // Skip the real constructor to avoid bootstrapping live models
    }

    public function setRequest(RequestInterface $request): self
    {
        $this->request = $request;
        return $this;
    }

    public function setResponse(ResponseInterface $response): self
    {
        $this->response = $response;
        return $this;
    }

    public function setUserModel(UserModel $model): self
    {
        $this->userModel = $model;
        return $this;
    }

    public function setIpAttemptModel(IpAttemptModel $model): self
    {
        $this->ipAttemptModel = $model;
        return $this;
    }

    public function setLoginActivityModel(LoginActivityModel $model): self
    {
        $this->loginActivityModel = $model;
        return $this;
    }

    protected function getUserRoleNames(int $userId): array
    {
        return ['admin'];
    }
}

class AuthControllerTest extends CIUnitTestCase
{
    private TestableAuthController $controller;
    private UserModel $userModel;
    private IpAttemptModel $ipAttemptModel;
    private LoginActivityModel $loginActivityModel;

    protected function setUp(): void
    {
        parent::setUp();
        helper('security');
        putenv('JWT_SECRET=test-secret-for-auth-controller');
        $_ENV['JWT_SECRET'] = 'test-secret-for-auth-controller';
        $_SERVER['JWT_SECRET'] = 'test-secret-for-auth-controller';

        $this->controller = new TestableAuthController();
        $this->controller->setResponse(service('response'));

        $this->userModel = $this->getMockBuilder(UserModel::class)
            ->disableOriginalConstructor()
            ->addMethods(['where'])
            ->onlyMethods(['first', 'update'])
            ->getMock();
        $this->ipAttemptModel = $this->getMockBuilder(IpAttemptModel::class)
            ->disableOriginalConstructor()
            ->addMethods(['where'])
            ->onlyMethods(['first'])
            ->getMock();
        $this->loginActivityModel = $this->getMockBuilder(LoginActivityModel::class)
            ->disableOriginalConstructor()
            ->onlyMethods(['insert'])
            ->getMock();

        $this->controller->setUserModel($this->userModel);
        $this->controller->setIpAttemptModel($this->ipAttemptModel);
        $this->controller->setLoginActivityModel($this->loginActivityModel);
    }

    public function testApiLoginReturnsTokenAndRoles()
    {
        $payload = [
            'email' => 'admin@example.com',
            'password' => '12345678',
        ];

        $user = [
            'id' => 16,
            'email' => $payload['email'],
            'firstname' => 'Admin',
            'lastname' => 'Panel',
            'password' => pbkdf2_hash($payload['password']),
            'is_suspended' => false,
            'user_type' => 'admin',
        ];

        $this->ipAttemptModel->method('where')->willReturnSelf();
        $this->ipAttemptModel->method('first')->willReturn(null);

        $this->userModel->method('where')->willReturnSelf();
        $this->userModel->method('first')->willReturn($user);
        $this->userModel->method('update')->willReturn(true);

        $this->loginActivityModel->method('insert')->willReturn(true);

        $this->controller->setRequest(new FakeAuthJsonRequest($payload));

        $response = $this->controller->apiLogin();
        $body = json_decode((string) $response->getBody(), true);

        $this->assertIsArray($body);
        $this->assertTrue($body['status'], json_encode($body));
        $this->assertArrayHasKey('token', $body);
        $this->assertArrayHasKey('user', $body);
        $this->assertSame($user['id'], $body['user']['id']);
        $this->assertArrayHasKey('roles', $body['user']);
        $this->assertTrue($body['user']['roles']['admin'] ?? false);
        $this->assertIsString($body['token']);
    }
}
