<?php
namespace App\Controllers\Patient;

use App\Traits\ResponseTrait;
use CodeIgniter\Controller;
use App\Libraries\ValueSet;
use App\Models\Patient\PatientModel;

class PatientController extends Controller {
    use ResponseTrait;

    protected $db;
    protected $model;
    protected $rules;

    public function __construct() {
        $this->db = \Config\Database::connect();
        $this->model = new PatientModel();
        $this->rules = [
            'PatientID'         => 'required|regex_match[/^[A-Za-z0-9.-]+$/]|max_length[30]',
            'AlternatePID'      => 'permit_empty|regex_match[/^[A-Za-z0-9.-]+$/]|max_length[30]',
            'Prefix'            => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[10]',
            'Sex'             => 'required',

            'NameFirst'         => 'required|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameMiddle'        => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameMaiden'        => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameLast'          => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',

            'Suffix'            => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[10]',
            'PlaceOfBirth'      => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[100]',
            'Citizenship'       => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[100]',

            'Street_1'          => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',
            'Street_2'          => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',
            'Street_3'          => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',

            'EmailAddress1'     => 'permit_empty|valid_email|max_length[100]',
            'EmailAddress2'     => 'permit_empty|valid_email|max_length[100]',
            
            'Birthdate'             => 'required',
            'PatIdt.IdentifierType' => 'permit_empty',
            'PatIdt.Identifier'     => 'permit_empty|max_length[255]',

            'ZIP'               => 'permit_empty|is_natural|max_length[10]',
            'Phone'             => 'permit_empty|regex_match[/^\\+?[0-9]{8,15}$/]',
            'MobilePhone'       => 'permit_empty|regex_match[/^\\+?[0-9]{8,15}$/]'
        ];
    }

    public function index() {
        $filters = [
            'InternalPID' => $this->request->getVar('InternalPID'),
            'PatientID'   => $this->request->getVar('PatientID'),
            'Name'        => $this->request->getVar('Name'),
            'Birthdate'   => $this->request->getVar('Birthdate'),
        ];

        try {
            $rows = $this->model->getPatients($filters);
            
            $rows = ValueSet::transformLabels($rows, [
                'Sex' => 'sex',
            ]);
            
            return $this->respond([ 'status' => 'success', 'message'=> "data fetched successfully", 'data'   => $rows ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Exception : '.$e->getMessage());
        }
    }

    public function show($InternalPID = null) {
        try {
            $row = $this->model->getPatient($InternalPID);
            if (empty($row)) { return $this->respond([ 'status'  => 'success', 'message' => "data not found.", 'data' => null ], 200); }
            
            $row = ValueSet::transformLabels([$row], [
                'Sex' => 'sex',
            ])[0];
            
            return $this->respond([ 'status'  => 'success', 'message' => "data fetched successfully", 'data' => $row ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function create() {
        $input = $this->request->getJSON(true);

        // Khusus untuk Override PATIDT
        $type = $input['PatIdt']['IdentifierType'] ?? null;
        $identifierRulesMap = $this->getPatIdtIdentifierRulesMap();
        if ($type === null || $type === '' || !is_string($type)) {
            $identifierRule = 'permit_empty|max_length[255]';
            $this->rules['PatIdt.IdentifierType'] = 'permit_empty';
            $this->rules['PatIdt.Identifier']     = $identifierRule;
        } else {
            $identifierRule = $identifierRulesMap[$type] ?? 'permit_empty|max_length[255]';
            $this->rules['PatIdt.IdentifierType'] = 'required';
            $this->rules['PatIdt.Identifier']     = $identifierRule;
        }

        if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
        try {
            $InternalPID = $this->model->createPatient($input);
            return $this->respondCreated([ 'status'  => 'success', 'message' => "data $InternalPID created successfully" ]);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    public function update($InternalPID = null) {
        $input = $this->request->getJSON(true) ?? [];

        if (!$InternalPID || !ctype_digit((string) $InternalPID)) {
            return $this->respond([
                'status' => 'error',
                'message' => 'InternalPID is required and must be a valid integer.'
            ], 400);
        }

        if (!is_array($input) || $input === []) {
            return $this->respond([
                'status' => 'failed',
                'message' => 'Patch payload is required.'
            ], 400);
        }

        if (array_key_exists('PatIdt', $input) && $input['PatIdt'] !== null && !is_array($input['PatIdt'])) {
            return $this->failValidationErrors([
                'PatIdt' => 'PatIdt must be an object or null.'
            ]);
        }

        $patchRules = $this->buildPatchRules($input);
        if ($patchRules !== [] && !$this->validateData($input, $patchRules)) {
            return $this->failValidationErrors($this->validator->getErrors());
        }

        try {
            $updatedPid = $this->model->updatePatientPartial((int) $InternalPID, $input);
            if ($updatedPid === null) {
                return $this->respond([
                    'status' => 'failed',
                    'message' => "data $InternalPID not found"
                ], 404);
            }

            return $this->respond([ 'status'  => 'success', 'message' => "data $updatedPid update successfully" ], 200);
        } catch (\Exception $e) {
            return $this->failServerError('Something went wrong: ' . $e->getMessage());
        }
    }

    private function buildPatchRules(array $input): array
    {
        $rules = [];

        $fieldRules = [
            'PatientID' => 'permit_empty|regex_match[/^[A-Za-z0-9.-]+$/]|max_length[30]',
            'AlternatePID' => 'permit_empty|regex_match[/^[A-Za-z0-9.-]+$/]|max_length[30]',
            'Prefix' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[10]',
            'Sex' => 'permit_empty',
            'NameFirst' => 'required|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameMiddle' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameMaiden' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'NameLast' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|min_length[1]|max_length[60]',
            'Suffix' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[10]',
            'PlaceOfBirth' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[100]',
            'Citizenship' => 'permit_empty|regex_match[/^[A-Za-z\'\. ]+$/]|max_length[100]',
            'Street_1' => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',
            'Street_2' => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',
            'Street_3' => 'permit_empty|regex_match[/^[A-Za-z0-9\'.,\/\- ]+$/]|max_length[255]',
            'EmailAddress1' => 'permit_empty|valid_email|max_length[100]',
            'EmailAddress2' => 'permit_empty|valid_email|max_length[100]',
            'Birthdate' => 'permit_empty',
            'ZIP' => 'permit_empty|is_natural|max_length[10]',
            'Phone' => 'permit_empty|regex_match[/^\\+?[0-9]{8,15}$/]',
            'MobilePhone' => 'permit_empty|regex_match[/^\\+?[0-9]{8,15}$/]',
            'Country' => 'permit_empty|max_length[10]',
            'Race' => 'permit_empty|max_length[100]',
            'MaritalStatus' => 'permit_empty',
            'Religion' => 'permit_empty|max_length[100]',
            'Ethnic' => 'permit_empty|max_length[100]',
            'isDead' => 'permit_empty',
            'TimeOfDeath' => 'permit_empty',
            'PatCom' => 'permit_empty|string',
            'PatAtt' => 'permit_empty',
            'LinkTo' => 'permit_empty',
            'Custodian' => 'permit_empty',
        ];

        foreach ($fieldRules as $field => $rule) {
            if (array_key_exists($field, $input) && $field !== 'PatIdt') {
                $rules[$field] = $rule;
            }
        }

        if (array_key_exists('PatIdt', $input) && $input['PatIdt'] !== null) {
            $type = $input['PatIdt']['IdentifierType'] ?? null;
            $identifierRulesMap = $this->getPatIdtIdentifierRulesMap();
            $identifierRule = is_string($type)
                ? ($identifierRulesMap[$type] ?? 'required|max_length[255]')
                : 'required|max_length[255]';

            $rules['PatIdt.IdentifierType'] = 'required';
            $rules['PatIdt.Identifier'] = $identifierRule;
        }

        return $rules;
    }

    private function getPatIdtIdentifierRulesMap(): array
    {
        return [
            'KTP'  => 'required|regex_match[/^[0-9]{16}$/]',
            'PASS' => 'required|regex_match[/^[A-Za-z0-9]{1,9}$/]',
            'SSN'  => 'required|regex_match[/^[0-9]{9}$/]',
            'SIM'  => 'required|regex_match[/^[0-9]{19,20}$/]',
            'KTAS' => 'required|regex_match[/^[0-9]{11}$/]',
        ];
    }
    
    public function delete() {
        try {
            $input = $this->request->getJSON(true);
            $InternalPID = $input["InternalPID"];

            // Mencegah Inputan 0, [], null, sql injection
            if (empty($InternalPID) || !ctype_digit((string) $InternalPID)) {
                return $this->respond([
                    'status' => 'error',
                    'message' => "Patient ID must be a valid integer."
                ], 400);
            }

            $patient = $this->db->table('patient')->where('InternalPID', $InternalPID)->get()->getRow();

            if (!$patient) {
                return $this->failNotFound("Patient ID with {$InternalPID} not found.");
            }

            $this->db->table('patient')->where('InternalPID', $InternalPID)->update(['DelDate' => date('Y-m-d H:i:s')]);

            return $this->respondDeleted([
                'status' => 'success',
                'message' => "Patient ID with {$InternalPID} deleted successfully."
            ]);
 
        } catch (\Exception $e) {
            return $this->failServerError("Internal server error: " . $e->getMessage());
        }
    }

    public function patientCheck() {
        try {
            $PatientID = $this->request->getVar('PatientID');
            $EmailAddress = $this->request->getVar('EmailAddress');
            $Phone = $this->request->getVar('Phone');

            if (!empty($PatientID)){
                if (!preg_match('/^[A-Za-z0-9.-]+$/', (string) $PatientID)) {
                    return $this->respond([
                        'status' => 'error',
                        'message' => 'PatientID format is invalid.',
                        'data' => null
                    ], 400);
                }

                $patient = $this->db->table('patient')
                    ->where('PatientID', $PatientID)
                    ->get()
                    ->getRowArray();
            } elseif (!empty($EmailAddress)) {
                $patient = $this->db->table('patient')
                    ->groupStart()
                        ->where('EmailAddress1', $EmailAddress)
                        ->orWhere('EmailAddress2', $EmailAddress)
                    ->groupEnd()
                    ->get()
                    ->getRowArray();
            } elseif (!empty($Phone)){
                $patient = $this->db->table('patient')
                    ->groupStart()
                        ->where('Phone', $Phone)
                        ->orWhere('MobilePhone', $Phone)
                    ->groupEnd()
                    ->get()
                    ->getRowArray();
            } else {
                return $this->respond([
                    'status' => 'error',
                    'message' => 'PatientID, EmailAddress, or Phone parameter is required.',
                    'data' => null
                ], 400); 
            }

            if (!$patient) {
                return $this->respond([
                    'status'  => 'success',
                    'message' => !empty($PatientID) ? 'PatientID not found.' : (!empty($Phone) ? 'Phone not found.' : 'EmailAddress not found.'),
                    'data'    => true,
                ], 200);
            }

            return $this->respond([
                'status'  => 'success',
                'message' => !empty($PatientID) ? 'PatientID already exists.' : (!empty($Phone) ? 'Phone already exists.' : 'EmailAddress already exists.'),
                'data'    => false,
            ], 200);


        } catch (\Exception $e) {
            // Error Server Mengembalikan 500
            return $this->failServerError('Something went wrong.'.$e->getMessage());
        }
    }
}
