<?php

namespace Tests\Feature\Test;

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

class TestMapPatchTest extends CIUnitTestCase
{
    use FeatureTestTrait;

    protected string $token;
    protected string $endpoint = 'api/test/testmap';

    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 createTestMap(array $data = []): array
    {
        $payload = array_merge([
            'HostType' => 'SITE',
            'HostID' => 1,
            'ClientType' => 'SITE',
            'ClientID' => 1,
        ], $data);

        $response = $this->withHeaders($this->authHeaders())
                         ->withBodyFormat('json')
                         ->call('post', $this->endpoint, $payload);

        $response->assertStatus(201);
        $created = json_decode($response->getJSON(), true);
        $id = $created['data'];

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

    public function testPartialUpdateTestMapSuccess()
    {
        $testMap = $this->createTestMap();
        $id = $testMap['TestMapID'];

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "{$this->endpoint}/{$id}", ['ClientType' => 'WST']);

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

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

        $this->assertEquals('WST', $showData['ClientType']);
        $this->assertEquals((string) $testMap['HostID'], (string) $showData['HostID']);
    }

    public function testPartialUpdateTestMapNotFound()
    {
        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "{$this->endpoint}/999999", ['ClientType' => 'WST']);

        $patch->assertStatus(404);
    }

    public function testPartialUpdateTestMapInvalidId()
    {
        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "{$this->endpoint}/invalid", ['ClientType' => 'WST']);

        $patch->assertStatus(400);
    }

    public function testPartialUpdateTestMapEmptyPayload()
    {
        $testMap = $this->createTestMap();
        $id = $testMap['TestMapID'];

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

        $patch->assertStatus(400);
    }

    public function testPartialUpdateTestMapSingleField()
    {
        $testMap = $this->createTestMap();
        $id = $testMap['TestMapID'];

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "{$this->endpoint}/{$id}", ['HostID' => 2]);

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

        $this->assertEquals('2', (string) $showData['HostID']);
        $this->assertEquals((string) $testMap['ClientID'], (string) $showData['ClientID']);
    }

    public function testCreateTestMapWithDetails()
    {
        $details = [
            [
                'HostTestCode' => 'HB',
                'HostTestName' => 'Hemoglobin',
                'ClientTestCode' => '2',
                'ClientTestName' => 'Hemoglobin',
            ],
            [
                'HostTestCode' => 'HCT',
                'HostTestName' => 'Hematocrit',
                'ClientTestCode' => '3',
                'ClientTestName' => 'Hematocrit',
            ],
        ];

        $testMap = $this->createTestMap([
            'HostType' => 'SITE',
            'HostID' => 1,
            'ClientType' => 'SITE',
            'ClientID' => 2,
            'details' => $details,
        ]);

        $this->assertCount(2, $testMap['details']);
        $this->assertEquals('2', $testMap['details'][0]['ClientTestCode']);
    }

    public function testPatchTestMapDetailOperations()
    {
        $initialDetails = [
            [
                'HostTestCode' => 'HB',
                'HostTestName' => 'Hemoglobin',
                'ClientTestCode' => '2',
                'ClientTestName' => 'Hemoglobin',
            ],
            [
                'HostTestCode' => 'HCT',
                'HostTestName' => 'Hematocrit',
                'ClientTestCode' => '3',
                'ClientTestName' => 'Hematocrit',
            ],
        ];

        $testMap = $this->createTestMap([
            'HostType' => 'SITE',
            'HostID' => 1,
            'ClientType' => 'SITE',
            'ClientID' => 1,
            'details' => $initialDetails,
        ]);

        $existingDetails = $testMap['details'];
        $editDetail = $existingDetails[0];
        $deleteDetail = $existingDetails[1];

        $patch = $this->withHeaders($this->authHeaders())
                      ->withBodyFormat('json')
                      ->call('patch', "{$this->endpoint}/{$testMap['TestMapID']}", [
                          'ClientType' => 'WST',
                          'details' => [
                              'edited' => [
                                  [
                                      'TestMapDetailID' => $editDetail['TestMapDetailID'],
                                      'ClientTestName' => 'Hemoglobin Updated',
                                  ],
                              ],
                              'created' => [
                                  [
                                       'HostTestCode' => 'MCV',
                                       'HostTestName' => 'MCV',
                                       'ClientTestCode' => '4',
                                       'ClientTestName' => 'MCV',
                                  ],
                              ],
                              'deleted' => [$deleteDetail['TestMapDetailID']],
                          ],
                      ]);

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

        $show = $this->withHeaders($this->authHeaders())->call('get', "{$this->endpoint}/{$testMap['TestMapID']}");
        $show->assertStatus(200);
        $showData = json_decode($show->getJSON(), true)['data'];

        $this->assertEquals('WST', $showData['ClientType']);
        $this->assertCount(2, $showData['details']);
        $detailIds = array_column($showData['details'], 'TestMapDetailID');
        $this->assertContains($editDetail['TestMapDetailID'], $detailIds);
        $this->assertNotContains($deleteDetail['TestMapDetailID'], $detailIds);

        $updatedDetails = array_values(array_filter($showData['details'], static fn ($row) => $row['TestMapDetailID'] === $editDetail['TestMapDetailID']));
        $this->assertNotEmpty($updatedDetails);
        $this->assertEquals('Hemoglobin Updated', $updatedDetails[0]['ClientTestName']);
    }

    public function testDeleteTestMapRemovesDetails()
    {
        $testMap = $this->createTestMap([
            'HostType' => 'SITE',
            'HostID' => 2,
            'ClientType' => 'SITE',
            'ClientID' => 3,
            'details' => [
                [
                    'HostTestCode' => 'PLT',
                    'HostTestName' => 'Platelet',
                    'ClientTestCode' => '5',
                    'ClientTestName' => 'Platelet',
                ],
            ],
        ]);

        $delete = $this->withHeaders($this->authHeaders())
                       ->withBodyFormat('json')
                       ->call('delete', $this->endpoint, ['TestMapID' => $testMap['TestMapID']]);

        $delete->assertStatus(200);

        $details = $this->withHeaders($this->authHeaders())
                            ->call('get', "{$this->endpoint}/detail/by-testmap/{$testMap['TestMapID']}");
        $details->assertStatus(200);
        $this->assertEquals([], json_decode($details->getJSON(), true)['data']);
    }
}
