<?php

namespace Tests\App\Controllers\Api;

use App\Controllers\Api\NotificationController;
use CodeIgniter\HTTP\URI;
use CodeIgniter\HTTP\UserAgent;
use CodeIgniter\Test\CIUnitTestCase;
use CodeIgniter\Test\Mock\MockIncomingRequest;
use Config\App;

class StubUserNotificationModel
{
    public array $whereHistory = [];
    private ?array $firstResult = null;
    public ?array $lastUpdate = null;

    public function select(string $columns): self
    {
        return $this;
    }

    public function join(string $table, string $cond): self
    {
        return $this;
    }

    public function where(string $column, $value = null): self
    {
        $this->whereHistory[] = ['column' => $column, 'value' => $value];
        return $this;
    }

    public function first(): ?array
    {
        return $this->firstResult;
    }

    public function update(int $id, array $data): bool
    {
        $this->lastUpdate = ['id' => $id, 'data' => $data];
        return true;
    }

    public function setFirstResult(?array $result): void
    {
        $this->firstResult = $result;
    }
}

class FakeNotificationRequest extends MockIncomingRequest
{
    public function __construct(private readonly array $params = [])
    {
        parent::__construct(config(App::class), new URI('https://test.alrahmaisgl.org'), null, new UserAgent());
    }

    public function getGet($key = null, $filter = null, $default = null)
    {
        if ($key === null) {
            return $this->params;
        }

        return $this->params[$key] ?? $default;
    }
}

class TestableNotificationController extends NotificationController
{
    private array $overridePagination = [];
    private ?object $currentUser = null;

    public function __construct()
    {
        // Skip normal boot to keep dependencies under test control.
    }

    public function setModels(StubUserNotificationModel $model): void
    {
        $this->userNotificationModel = $model;
    }

    public function setCurrentUser(object $user): void
    {
        $this->currentUser = $user;
    }

    public function setPaginatedResult(array $result): void
    {
        $this->overridePagination = $result;
    }

    protected function paginate($source, int $page = 1, int $perPage = 20): array
    {
        if (!empty($this->overridePagination)) {
            return $this->overridePagination;
        }

        return parent::paginate($source, $page, $perPage);
    }

    protected function getCurrentUser(): ?object
    {
        return $this->currentUser;
    }
}

class NotificationControllerTest extends CIUnitTestCase
{
    private TestableNotificationController $controller;
    private StubUserNotificationModel $model;

    protected function setUp(): void
    {
        parent::setUp();

        $this->controller = new TestableNotificationController();
        $this->model = new StubUserNotificationModel();
        $this->controller->setModels($this->model);
        $this->controller->setCurrentUser((object) ['id' => 42]);
    }

    public function testIndexReturnsPaginatedNotifications()
    {
        $payload = ['data' => [['id' => 1]], 'pagination' => ['total' => 1]];
        $this->controller->setPaginatedResult($payload);
        $request = new FakeNotificationRequest();
        $this->controller->setRequest($request);

        $response = $this->controller->index();

        $this->assertTrue($response['status']);
        $this->assertSame($payload, $response['data']);
        $this->assertSame('Notifications retrieved successfully', $response['message']);
    }

    public function testIndexRespectsReadFilter()
    {
        $payload = ['data' => [], 'pagination' => ['total' => 0]];
        $this->controller->setPaginatedResult($payload);
        $request = new FakeNotificationRequest(['read' => 'false']);
        $this->controller->setRequest($request);

        $this->controller->index();

        $readWhere = array_filter(
            $this->model->whereHistory,
            static fn ($entry) => $entry['column'] === 'user_notifications.is_read'
        );

        $this->assertNotEmpty($readWhere);
        $this->assertSame(0, $readWhere[array_key_last($readWhere)]['value']);
    }

    public function testShowReturnsNotification()
    {
        $this->model->setFirstResult(['id' => 99, 'notification_id' => 99]);

        $response = $this->controller->show(99);

        $this->assertTrue($response['status']);
        $this->assertSame('Notification retrieved successfully', $response['message']);
    }

    public function testShowReturns404WhenMissing()
    {
        $this->model->setFirstResult(null);

        $response = $this->controller->show(9);

        $this->assertFalse($response['status']);
        $this->assertSame(404, $response['code']);
    }

    public function testMarkReadUpdatesNotification()
    {
        $this->model->setFirstResult(['id' => 50, 'notification_id' => 500]);

        $response = $this->controller->markRead(500);

        $this->assertTrue($response['status']);
        $this->assertSame('Notification marked as read', $response['message']);
        $this->assertSame(50, $this->model->lastUpdate['id']);
        $this->assertSame(1, $this->model->lastUpdate['data']['is_read']);
        $this->assertNotEmpty($this->model->lastUpdate['data']['read_at']);
    }

    public function testMarkReadReturns404WhenNotFound()
    {
        $this->model->setFirstResult(null);

        $response = $this->controller->markRead(77);

        $this->assertFalse($response['status']);
        $this->assertSame(404, $response['code']);
    }
}
