<?php

namespace Tests\Feature;

use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
use Firebase\JWT\JWT;

class MasterDataPatchTest extends CIUnitTestCase
{
    use FeatureTestTrait;

    protected string $token;

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

        $key = getenv('JWT_SECRET') ?: 'my-secret-key';
        $payload = [
            'iss' => 'localhost',
            'aud' => 'localhost',
            'iat' => time(),
            'nbf' => time(),
            'exp' => time() + 3600,
            'uid' => 1,
            'email' => 'admin@admin.com',
        ];

        $this->token = JWT::encode($payload, $key, 'HS256');
    }

    private function authHeaders(): array
    {
        return ['Cookie' => 'token=' . $this->token];
    }

    private function createResource(string $endpoint, array $payload)
    {
        $response = $this->withHeaders($this->authHeaders())
                         ->withBody(json_encode($payload))
                         ->call('post', $endpoint);

        $response->assertStatus(201);
        $decoded = json_decode($response->getJSON(), true);

        $this->assertEquals('success', $decoded['status']);

        return $decoded['data'];
    }

    private function fetchFirstRecord(string $endpoint, string $idKey): array
    {
        try {
            $response = $this->withHeaders($this->authHeaders())->call('get', $endpoint);
        } catch (PageNotFoundException $e) {
            $this->markTestSkipped("{$endpoint} GET not available: {$e->getMessage()}");
        }

        $response->assertStatus(200);
        $decoded = json_decode($response->getJSON(), true);
        $rows = $decoded['data'] ?? [];

        if (empty($rows)) {
            $this->markTestSkipped("No data available at {$endpoint}");
        }

        $record = $rows[0];
        $this->assertArrayHasKey($idKey, $record);

        return $record;
    }

    private function fetchResource(string $endpoint, $id): array
    {
        try {
            $response = $this->withHeaders($this->authHeaders())
                             ->call('get', "$endpoint/{$id}");
        } catch (PageNotFoundException $e) {
            $this->markTestSkipped("{$endpoint}/{$id} GET not available: {$e->getMessage()}");
        }

        $response->assertStatus(200);
        $decoded = json_decode($response->getJSON(), true);

        return $decoded['data'];
    }

    public function testPartialUpdateOccupation()
    {
        $occCode = 'PATCH_OCC_' . uniqid();
        $id = $this->createResource('api/occupation', [
            'OccCode' => $occCode,
            'OccText' => 'Original text',
        ]);

        $originalData = $this->fetchResource('api/occupation', $id);
        $originalCode = $originalData['OccCode'];

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/occupation/{$id}", ['OccText' => 'Patched occupation']);

        $patch->assertStatus(201);
        $patchData = json_decode($patch->getJSON(), true);
        $this->assertEquals('success', $patchData['status']);

        $showData = $this->fetchResource('api/occupation', $id);

        $this->assertEquals('Patched occupation', $showData['OccText']);
        $this->assertEquals($originalCode, $showData['OccCode']);
    }

    public function testPartialUpdateMedicalSpecialty()
    {
        $text = 'Specialty ' . uniqid();
        $id = $this->createResource('api/medicalspecialty', ['SpecialtyText' => $text]);

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/medicalspecialty/{$id}", ['SpecialtyText' => 'Updated specialty']);

        $patch->assertStatus(201);
        $show = $this->withHeaders($this->authHeaders())->call('get', "api/medicalspecialty/{$id}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('Updated specialty', $showData['SpecialtyText']);
    }

    public function testPartialUpdateCounter()
    {
        $initial = 'Counter ' . uniqid();
        $id = $this->createResource('api/counter', [
            'CounterName' => $initial,
            'CounterValue' => 1,
            'CounterStart' => 1,
            'CounterEnd' => 10,
            'CounterReset' => 1,
        ]);

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/counter/{$id}", ['CounterName' => 'Updated counter']);

        $patch->assertStatus(201);
        $show = $this->withHeaders($this->authHeaders())->call('get', "api/counter/{$id}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('Updated counter', $showData['CounterName']);
        $this->assertEquals(1, (int) $showData['CounterValue']);
    }

    public function testPartialUpdateOrganizationAccount()
    {
        $name = 'Account ' . uniqid();
        $id = $this->createResource('api/organization/account', ['AccountName' => $name]);

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/organization/account/{$id}", ['AccountName' => 'Updated account']);

        $patch->assertStatus(200);
        $show = $this->withHeaders($this->authHeaders())->call('get', "api/organization/account/{$id}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('Updated account', $showData['AccountName']);
    }

    public function testPartialUpdateDiscipline()
    {
        $code = 'DIS_' . strtoupper(bin2hex(random_bytes(2)));
        $name = 'Discipline ' . uniqid();
        $id = $this->createResource('api/organization/discipline', [
            'DisciplineCode' => $code,
            'DisciplineName' => $name,
        ]);

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/organization/discipline/{$id}", ['DisciplineName' => 'Discipline Updated']);

        $patch->assertStatus(201);
        $show = $this->withHeaders($this->authHeaders())->call('get', "api/organization/discipline/{$id}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('Discipline Updated', $showData['DisciplineName']);
        $this->assertEquals($code, $showData['DisciplineCode']);
    }

    public function testPartialUpdateCodingSystem()
    {
        $abbr = 'CS' . strtoupper(bin2hex(random_bytes(2)));
        $full = 'Full text ' . uniqid();
        $id = $this->createResource('api/organization/codingsys', [
            'CodingSysAbb' => $abbr,
            'FullText' => $full,
        ]);

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/organization/codingsys/{$id}", ['FullText' => 'Updated full text']);

        $patch->assertStatus(201);
        $show = $this->withHeaders($this->authHeaders())->call('get', "api/organization/codingsys/{$id}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('Updated full text', $showData['FullText']);
        $this->assertEquals($abbr, $showData['CodingSysAbb']);
    }

    public function testPartialUpdateSpecimenContainer()
    {
        $record = $this->fetchFirstRecord('api/specimen/container', 'ConDefID');
        $id = $record['ConDefID'];
        $newName = 'Patch Container ' . uniqid();

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/specimen/container/{$id}", ['ConName' => $newName]);

        $patch->assertStatus(201);
        $showData = $this->fetchResource('api/specimen/container', $id);

        $this->assertEquals($newName, $showData['ConName']);
        $this->assertEquals($record['ConCode'] ?? null, $showData['ConCode'] ?? null);
    }

    public function testPartialUpdateSpecimenPrep()
    {
        $record = $this->fetchFirstRecord('api/specimen/prep', 'SpcPrpID');
        $id = $record['SpcPrpID'];
        $newDesc = 'Partial Prep ' . uniqid();

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/specimen/prep/{$id}", ['Description' => $newDesc]);

        $patch->assertStatus(201);
        $showData = $this->fetchResource('api/specimen/prep', $id);

        $this->assertEquals($newDesc, $showData['Description']);
        $this->assertEquals($record['SpcStaID'] ?? null, $showData['SpcStaID'] ?? null);
    }

    public function testPartialUpdateSpecimenStatus()
    {
        $record = $this->fetchFirstRecord('api/specimen/status', 'SpcStaID');
        $id = $record['SpcStaID'];
        $newStatus = 'UpdatedStatus';

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/specimen/status/{$id}", ['SpcStatus' => $newStatus]);

        $patch->assertStatus(201);
        $showData = $this->fetchResource('api/specimen/status', $id);

        $this->assertEquals($newStatus, $showData['SpcStatus']);
        $this->assertEquals($record['OrderID'] ?? null, $showData['OrderID'] ?? null);
    }

    public function testPartialUpdateSpecimenCollection()
    {
        $record = $this->fetchFirstRecord('api/specimen/collection', 'SpcColID');
        $id = $record['SpcColID'];
        $newBodySite = 'BodySite ' . uniqid();

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/specimen/collection/{$id}", ['BodySite' => $newBodySite]);

        $patch->assertStatus(201);
        $showData = $this->fetchResource('api/specimen/collection', $id);

        $this->assertEquals($newBodySite, $showData['BodySite']);
        $this->assertEquals($record['SpRole'] ?? null, $showData['SpRole'] ?? null);
    }

    public function testPartialUpdateEquipmentList()
    {
        $record = $this->fetchFirstRecord('api/equipmentlist', 'EID');
        $id = $record['EID'];
        $newName = 'Equipment ' . uniqid();

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "api/equipmentlist/{$id}", ['InstrumentName' => $newName]);

        $patch->assertStatus(200, 'Equipment patch should return 200');
        $showData = $this->fetchResource('api/equipmentlist', $id);

        $this->assertEquals($newName, $showData['InstrumentName']);
        $this->assertEquals($record['DepartmentID'] ?? null, $showData['DepartmentID'] ?? null);
    }
}
