<?php

namespace Tests\App\Controllers\Api;

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

class FakeTrackingRequest extends MockIncomingRequest
{
    public function __construct(private readonly ?array $json = null, private readonly array $gets = [])
    {
        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->json;
    }

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

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

class StubTrackingModel
{
    public array $findAllResult = [];
    public array $findResult = [];
    public array $updated = [];
    public array $inserted = [];
    public array $firstQueue = [];

    public function where($column, $value = null)
    {
        return $this;
    }

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

    public function groupBy($column)
    {
        return $this;
    }

    public function having($column, $value)
    {
        return $this;
    }

    public function findAll(): array
    {
        return $this->findAllResult;
    }

    public function orderBy(...$args)
    {
        return $this;
    }

    public function first()
    {
        return array_shift($this->firstQueue) ?? null;
    }

    public function queueFirst(array $row): void
    {
        $this->firstQueue[] = $row;
    }

    public function insert(array $data)
    {
        $this->inserted[] = $data;
        return 1;
    }

    public function getInsertID()
    {
        return 1;
    }

    public function update(int $id, array $data)
    {
        $this->updated[$id] = $data;
    }

    public function find(int $id)
    {
        return $this->findResult[$id] ?? null;
    }
}

class TestableAttendanceTrackingController extends AttendanceTrackingController
{
    public bool $validationResult = true;
    public array $validationErrors = [];
    public $validator;

    public function __construct()
    {
        // bypass real model instantiation
    }

    public function setTrackingModel(StubTrackingModel $model): void
    {
        $this->trackingModel = $model;
    }

    public function setValidationResult(bool $result, array $errors = []): void
    {
        $this->validationResult = $result;
        $this->validationErrors = $errors;
    }

    protected function validate($rules, array $messages = []): bool
    {
        $this->validator = new class($this->validationErrors) {
            private array $errors;

            public function __construct(array $errors)
            {
                $this->errors = $errors;
            }

            public function getErrors()
            {
                return $this->errors;
            }
        };

        return $this->validationResult;
    }

    protected function respond($data = null, ?int $status = null, string $message = '')
    {
        return ['data' => $data, 'code' => $status, 'message' => $message];
    }
}

class AttendanceTrackingControllerTest extends CIUnitTestCase
{
    private TestableAttendanceTrackingController $controller;
    private StubTrackingModel $model;

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

        $this->controller = new TestableAttendanceTrackingController();
        $this->model = new StubTrackingModel();
        $this->controller->setTrackingModel($this->model);
    }

    public function testRecordValidationFails()
    {
        $this->controller->setValidationResult(false, ['student_id' => 'required']);
        $this->controller->setRequest(new FakeTrackingRequest(['student_id' => null]));

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

        $this->assertSame(422, $response['code']);
        $this->assertArrayHasKey('student_id', $response['errors']);
    }

    public function testRecordUpdatesExisting()
    {
        $this->model->queueFirst(['id' => 5]);
        $request = new FakeTrackingRequest(['student_id' => 1, 'date' => '2024-09-01', 'status' => 'present']);
        $this->controller->setRequest($request);

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

        $this->assertSame('Attendance tracking record updated successfully', $response['message']);
        $this->assertSame(['student_id' => 1, 'date' => '2024-09-01', 'status' => 'present'], $this->model->updated[5]);
    }

    public function testRecordCreatesNew()
    {
        $request = new FakeTrackingRequest(['student_id' => 2, 'date' => '2024-09-02', 'status' => 'late']);
        $this->controller->setRequest($request);
        $this->controller->setValidationResult(true);

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

        $this->assertSame('Attendance tracking record created successfully', $response['message']);
        $this->assertSame(1, $response['data']['id']);
    }

    public function testViolationsDefaultsThreshold()
    {
        $this->controller->setRequest(new FakeTrackingRequest(null, []));
        $this->model->findAllResult = [['student_id' => 3, 'total_violations' => 5]];
        $response = $this->controller->violations();

        $this->assertSame('Attendance violations retrieved successfully', $response['message']);
        $this->assertSame(1, count($response['data']));
    }

    public function testViolationsCustomThreshold()
    {
        $request = new FakeTrackingRequest(null, ['threshold' => '2']);
        $this->controller->setRequest($request);
        $this->model->findAllResult = [['student_id' => 4, 'total_violations' => 3]];

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

        $this->assertSame(1, count($response['data']));
    }

    public function testStudentRecordsOrder()
    {
        $this->model->findAllResult = [['id' => 7]];
        $response = $this->controller->student(7);

        $this->assertSame('Tracking records retrieved successfully for student', $response['message']);
        $this->assertSame(7, $response['data'][0]['id']);
    }
}
