<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Repositories\PatientRepository;
use InvalidArgumentException;
use RuntimeException;

final class PatientService
{
    public function __construct(private ?PatientRepository $patients=null) { $this->patients ??= new PatientRepository(); }

    public function list(array $filters, ?int $therapistId = null): array
    {
        $search = trim((string)($filters['search']??''));
        $status = trim((string)($filters['status']??''));
        $page = max(1,(int)($filters['page']??1));
        return $therapistId !== null
            ? $this->patients->paginateForTherapist($therapistId, $search, $status, $page, 25)
            : $this->patients->paginate($search, $status, $page, 25);
    }

    public function find(int $id): array
    {
        $patient=$this->patients->find($id);
        if ($patient===null) throw new RuntimeException('Patient not found.',404);
        return $patient;
    }

    public function create(array $data, int $actorId): int
    {
        $this->validate($data);
        $pdo=Database::connection();
        $pdo->beginTransaction();
        try {
            $reference=$this->patients->nextSequence('patient.reference','PT-',6);
            $id=$this->patients->create($this->normalize($data,$reference));
            $this->patients->audit($actorId,'patient_created',$id,['patient_reference'=>$reference]);
            $pdo->commit();
            return $id;
        } catch (\Throwable $e) { if($pdo->inTransaction())$pdo->rollBack(); throw $e; }
    }

    public function delete(int $id, int $actorId): void
    {
        $this->find($id);
        $this->patients->softDelete($id);
        $this->patients->audit($actorId,'patient_archived',$id);
    }

    public function update(int $id, array $data, int $actorId): void
    {
        $this->find($id); $this->validate($data);
        $this->patients->update($id,$this->normalize($data));
        $this->patients->audit($actorId,'patient_updated',$id,['status'=>$data['status']??null]);
    }

    private function normalize(array $d, ?string $reference=null): array
    {
        return [
            'patient_reference'=>$reference,
            'first_name'=>trim((string)($d['first_name']??'')), 'last_name'=>trim((string)($d['last_name']??'')),
            'date_of_birth'=>($d['date_of_birth']??'')!==''?$d['date_of_birth']:null,
            'gender'=>trim((string)($d['gender']??''))?:null, 'phone'=>trim((string)($d['phone']??''))?:null,
            'email'=>trim((string)($d['email']??''))?:null, 'address'=>trim((string)($d['address']??''))?:null,
            'parent_guardian'=>trim((string)($d['parent_guardian']??''))?:null,
            'emergency_contact'=>trim((string)($d['emergency_contact']??''))?:null,
            'status'=>in_array((string)($d['status']??'active'),['active','inactive','discharged'],true)?(string)$d['status']:'active'
        ];
    }

    private function validate(array $d): void
    {
        if (trim((string)($d['first_name']??''))==='') throw new InvalidArgumentException('First name is required.');
        if (trim((string)($d['last_name']??''))==='') throw new InvalidArgumentException('Last name is required.');
        if (($d['email']??'')!=='' && !filter_var((string)$d['email'],FILTER_VALIDATE_EMAIL)) throw new InvalidArgumentException('Enter a valid email address.');
        if (($d['date_of_birth']??'')!=='' && !preg_match('/^\d{4}-\d{2}-\d{2}$/',(string)$d['date_of_birth'])) throw new InvalidArgumentException('Enter a valid date of birth.');
        if (!in_array((string)($d['status']??'active'),['active','inactive','discharged'],true)) throw new InvalidArgumentException('Invalid patient status.');
    }
}
