<?php

namespace Tests\Feature\Patients;

use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
use Faker\Factory;

class PatientCreateTest extends CIUnitTestCase
{
    use FeatureTestTrait;
    protected $endpoint = 'api/patient';

    protected function setUp(): void
    {
        parent::setUp();
    }
    
    // 400 - Passed 
    // Validation Gagal - Array Tidak Complete
    public function testCreatePatientValidationFail() {

        // Test dengan payload yg tidak lengkap
        // error 400 yg diharapkan
        $payload = ['Name' => 'Ngawur'];
        $result = $this->withBodyFormat('json')->call('post', 'api/patient', $payload);
        $result->assertStatus(400);
        
        // Test dengan PatiD yg sudah ada
        // Kondisi Jika PatiD Sama
        $payload = [
            "PatientID"=> "SMAJ1",
            "AlternatePID"=> "ALT001234",
            "Prefix"=> "Mr.",
            "NameFirst"=> "Budi",
            "NameMiddle"=> "Santoso",
            "NameMaiden"=> "Kiki",
            "NameLast"=> "Wijaya",
            "Suffix"=> "S.kom",
            "NameAlias"=> "Bud",
            "Sex"=> "1",
        ];
        $result = $this->withBodyFormat('json')->call('post', 'api/patient', $payload);
        $result->assertStatus(400);

    }

    // 201 - Passed
    // Test Sukses Dengan User Dummy
    public function testCreatePatientSuccess() {

        $faker = Factory::create('id_ID');
        
        for ($i = 0; $i < 2; $i++) {
            $payload = [
                "PatientID"      => "DUM" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
                "AlternatePID"   => "DMY" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
                "Prefix"         => $faker->title,
                "NameFirst"      => $faker->firstName,
                "NameMiddle"     => $faker->firstName,
                "NameMaiden"     => $faker->firstName,
                "NameLast"       => $faker->lastName,
                "Suffix"         => "S.Kom",
                "NameAlias"      => $faker->userName,
                "Sex"         => $faker->numberBetween(5, 6),
                "PlaceOfBirth"   => $faker->city,
                "Birthdate"      => $faker->date('Y-m-d'),
                "ZIP"            => $faker->postcode,
                "Street_1"       => $faker->streetAddress,
                "Street_2"       => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
                "Street_3"       => "Blok " . $faker->numberBetween(1, 20),
                "City"           => $faker->city,
                "Province"       => $faker->state,
                "EmailAddress1"  => "A" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com',
                "EmailAddress2"  => "B" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com',
                "Phone"          => $faker->numerify('08##########'),
                "MobilePhone"    => $faker->numerify('08##########'),
                "Race"           => (string) $faker->numberBetween(175, 205),
                "Country"        => (string) $faker->numberBetween(221, 469),
                "MaritalStatus"  => (string) $faker->numberBetween(8, 15),
                "Religion"       => (string) $faker->numberBetween(206, 212),
                "Ethnic"         => (string) $faker->numberBetween(213, 220),
                "Citizenship"    => "WNI",
                "isDead" => (string) $faker->numberBetween(0, 1),
                "LinkTo"         => (string) $faker->numberBetween(2, 3),
                "Custodian"      => $i-1,
                "PatIdt" => [
                    "IdentifierType" => "KTP",
                    "Identifier"     => $faker->nik() ?? $faker->numerify('################')
                ],
                "PatAtt" => [
                    [ "Address" => "/api/upload/" . $faker->uuid . ".jpg" ]
                ],
                "PatCom" => $faker->sentence,
            ];
            if($payload['isDead'] == '1') {
                $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
            } else {
                $payload['DeathDateTime'] = null;
            }

            $result = $this->withBodyFormat('json')->call('post', 'api/patient', $payload);



            $result->assertStatus(201);
        }
    }

    // Error - Passed
    // Test dengan user dummy dan harus error
    public function testCreatePatidtValidationError() {

        $faker = Factory::create('id_ID');

        $payload = [
            "PatientID"      => "DUMQ" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
            "AlternatePID"   => "DMYQ" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
            "Prefix"         => $faker->title,
            "NameFirst"      => $faker->firstName,
            "NameMiddle"     => $faker->firstName,
            "NameMaiden"     => $faker->firstName,
            "NameLast"       => $faker->lastName,
            "Suffix"         => "S.Kom",
            "NameAlias"      => $faker->userName,
            "Sex"         => $faker->numberBetween(5, 6),
            "PlaceOfBirth"   => $faker->city,
            "Birthdate"      => $faker->date('Y-m-d'),
            "ZIP"            => $faker->postcode,
            "Street_1"       => $faker->streetAddress,
            "Street_2"       => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
            "Street_3"       => "Blok " . $faker->numberBetween(1, 20),
            "City"           => $faker->city,
            "Province"       => $faker->state,
            "EmailAddress1"  => "AAQ" . (string)$faker->numberBetween(1, 1110).'@gmail.com',
            "EmailAddress2"  => "BAQ" . (string)$faker->numberBetween(1, 1110).'@gmail.com',
            "Phone"          => $faker->numerify('08##########'),
            "MobilePhone"    => $faker->numerify('08##########'),
            "Race"           => (string) $faker->numberBetween(175, 205),
            "Country"        => (string) $faker->numberBetween(221, 469),
            "MaritalStatus"  => (string) $faker->numberBetween(8, 15),
            "Religion"       => (string) $faker->numberBetween(206, 212),
            "Ethnic"         => (string) $faker->numberBetween(213, 220),
            "Citizenship"    => "WNI",
            "isDead" => (string) $faker->numberBetween(0, 1),
            "LinkTo"         => (string) $faker->numberBetween(2, 3),
            "Custodian"      => 1,
            "PatIdt" => [
                "IdentifierType" => "KTP",
                // Identifier lebih dari 255 karakter - error validasi
                "Identifier"     => "numberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetween"
            ],
            "PatAtt" => [
                [ "Address" => "/api/upload/" . $faker->word . ".jpg" ]
            ],
            "PatCom" => $faker->sentence,
        ];

        if($payload['isDead'] == '1') {
            $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
        } else {
            $payload['DeathDateTime'] = null;
        }

        $result = $this->withBodyFormat('json')->post($this->endpoint, $payload);
        $json = $result->getJSON();
        $data = json_decode($json, true);
        
        $this->assertArrayHasKey("messages", $data);
    }

    public function testCreateWithoutAttachments() {
        $faker = Factory::create('id_ID');

        $payload = [
            "PatientID"      => "DUAU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
            "AlternatePID"   => "DMAU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000),
            "Prefix"         => $faker->title,
            "NameFirst"      => $faker->firstName,
            "NameMiddle"     => $faker->firstName,
            "NameMaiden"     => $faker->firstName,
            "NameLast"       => $faker->lastName,
            "Suffix"         => "S.Kom",
            "NameAlias"      => $faker->userName,
            "Sex"         => $faker->numberBetween(5, 6),
            "PlaceOfBirth"   => $faker->city,
            "Birthdate"      => $faker->date('Y-m-d'),
            "ZIP"            => $faker->postcode,
            "Street_1"       => $faker->streetAddress,
            "Street_2"       => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
            "Street_3"       => "Blok " . $faker->numberBetween(1, 20),
            "City"           => $faker->city,
            "Province"       => $faker->state,
            "EmailAddress1"  => "AiA" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "EmailAddress2"  => "BiA" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "Phone"          => $faker->numerify('08##########'),
            "MobilePhone"    => $faker->numerify('08##########'),
            "Race"           => (string) $faker->numberBetween(175, 205),
            "Country"        => (string) $faker->numberBetween(221, 469),
            "MaritalStatus"  => (string) $faker->numberBetween(8, 15),
            "Religion"       => (string) $faker->numberBetween(206, 212),
            "Ethnic"         => (string) $faker->numberBetween(213, 220),
            "Citizenship"    => "WNI",
            "isDead" => (string) $faker->numberBetween(0, 1),
            "LinkTo"         => (string) $faker->numberBetween(2, 3),
            "Custodian"      => 1,
            "PatIdt" => [
                "IdentifierType" => "KTP",
                "Identifier"     => $faker->nik() ?? $faker->numerify('################')
            ],
            "PatAtt" => [],
            "PatCom" => $faker->sentence,
        ];

        if($payload['isDead'] == '1') {
            $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
        } else {
            $payload['DeathDateTime'] = null;
        }

        $result = $this->withBodyFormat('json')->post($this->endpoint, $payload);

        $result->assertStatus(201);
    }

    // 201 - Passed
    // Test dengan user dummy dan harus berhasil - PatCom kosong
    public function testCreateWithoutPatComments() {
        $faker = Factory::create('id_ID');

        $payload = [
            "PatientID"      => "DUALU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
            "AlternatePID"   => "DMALU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000),
            "Prefix"         => $faker->title,
            "NameFirst"      => $faker->firstName,
            "NameMiddle"     => $faker->firstName,
            "NameMaiden"     => $faker->firstName,
            "NameLast"       => $faker->lastName,
            "Suffix"         => "S.Kom",
            "NameAlias"      => $faker->userName,
            "Sex"         => $faker->numberBetween(5, 6),
            "PlaceOfBirth"   => $faker->city,
            "Birthdate"      => $faker->date('Y-m-d'),
            "ZIP"            => $faker->postcode,
            "Street_1"       => $faker->streetAddress,
            "Street_2"       => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
            "Street_3"       => "Blok " . $faker->numberBetween(1, 20),
            "City"           => $faker->city,
            "Province"       => $faker->state,
            "EmailAddress1"  => "AiAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "EmailAddress2"  => "BiAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "Phone"          => $faker->numerify('08##########'),
            "MobilePhone"    => $faker->numerify('08##########'),
            "Race"           => (string) $faker->numberBetween(175, 205),
            "Country"        => (string) $faker->numberBetween(221, 469),
            "MaritalStatus"  => (string) $faker->numberBetween(8, 15),
            "Religion"       => (string) $faker->numberBetween(206, 212),
            "Ethnic"         => (string) $faker->numberBetween(213, 220),
            "Citizenship"    => "WNI",
            "isDead" => (string) $faker->numberBetween(0, 1),
            "LinkTo"         => (string) $faker->numberBetween(2, 3),
            "Custodian"      => 1,
            "PatIdt" => [
                "IdentifierType" => "KTP",
                "Identifier"     => $faker->nik() ?? $faker->numerify('################')
            ],
            "PatAtt" => [
                [ "Address" => "/api/upload/test1_" . $faker->uuid . ".jpg" ],
                [ "Address" => "/api/upload/test2_" . $faker->uuid . ".jpg" ],
                [ "Address" => "/api/upload/test3_" . $faker->uuid . ".jpg" ]
            ],
            "PatCom" => null,
        ];

        if($payload['isDead'] == '1') {
            $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
        } else {
            $payload['DeathDateTime'] = null;
        }

        $result = $this->withBodyFormat('json')->post($this->endpoint, $payload);

        $result->assertStatus(201);
    }

    // 500 - Passed
    // Test dengan user dummy dan harus gagal - identifier tidak valid
    public function testCreateDatabaseError() {
        $faker = Factory::create('id_ID');

        $payload = [
            "PatientID"      => "DaUALU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000),
            "AlternatePID"   => "DaMALU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000),
            "Prefix"         => $faker->title,
            "NameFirst"      => $faker->firstName,
            "NameMiddle"     => $faker->firstName,
            "NameMaiden"     => $faker->firstName,
            "NameLast"       => $faker->lastName,
            "Suffix"         => "S.Kom",
            "NameAlias"      => $faker->userName,
            "Sex"         => $faker->numberBetween(5, 6),
            "PlaceOfBirth"   => $faker->city,
            "Birthdate"      => $faker->date('Y-m-d'),
            "ZIP"            => $faker->postcode,
            "Street_1"       => $faker->streetAddress,
            "Street_2"       => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10),
            "Street_3"       => "Blok " . $faker->numberBetween(1, 20),
            "City"           => $faker->city,
            "Province"       => $faker->state,
            "EmailAddress1"  => "AiaAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "EmailAddress2"  => "BiaAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com',
            "Phone"          => $faker->numerify('08##########'),
            "MobilePhone"    => $faker->numerify('08##########'),
            "Race"           => (string) $faker->numberBetween(175, 205),
            "Country"        => (string) $faker->numberBetween(221, 469),
            "MaritalStatus"  => (string) $faker->numberBetween(8, 15),
            "Religion"       => (string) $faker->numberBetween(206, 212),
            "Ethnic"         => (string) $faker->numberBetween(213, 220),
            "Citizenship"    => "WNI",
            "isDead" => (string) $faker->numberBetween(0, 1),
            "LinkTo"         => (string) $faker->numberBetween(2, 3),
            "Custodian"      => 1,
            "PatIdt" => [
                "IdentifierType" => [], // Ini Salah
                "Identifier"     => $faker->nik() ?? $faker->numerify('################')
            ],
            "PatAtt" => [
                [ "Address" => "/api/upload/asasd" . $faker->word . ".jpg" ],
                [ "Address" => "/api/upload/adsds" . $faker->word . ".jpg" ],
                [ "Address" => "/api/upload/sdjs" . $faker->word . ".jpg" ]
            ],
            "PatCom" => [], 
        ];

        if($payload['isDead'] == '1') {
            $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s');
        } else {
            $payload['DeathDateTime'] = null;
        }
        
        $result = $this->withBodyFormat('json')->post($this->endpoint, $payload);
        $json = $result->getJSON();
        $data = json_decode($json, true);
        
        // Since we fixed the controller, this might return success or validation error, but not crash.
        // If success (201), data has 'status'=>'success'. If validation error (400), data has 'messages'.
        // Let's just assert it is NOT 500 first.
        if ($result->response()->getStatusCode() !== 500) {
              $this->fail("Expected 500 Server Error but got " . $result->response()->getStatusCode() . " Body: " . $json);
        }
        $this->assertEquals(500, $result->response()->getStatusCode());
    }

}
