<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\ReportRepository;
use InvalidArgumentException;
use RuntimeException;

final class ReportService
{
    public function __construct(private ?ReportRepository $reports=null){$this->reports??=new ReportRepository();}
    public function list(array $filters,?int $therapistId=null):array{return $this->reports->paginate($filters,$therapistId,25);}
    public function find(int $id):array{$r=$this->reports->find($id);if($r===null)throw new RuntimeException('Report not found.',404);return $r;}
    public function versions(int $id):array{return $this->reports->versions($id);}
    public function formData(?int $therapistId=null):array{return ['patients'=>$therapistId!==null?$this->reports->patientsForTherapist($therapistId):$this->reports->allPatients(),'therapists'=>$therapistId!==null?array_values(array_filter($this->reports->therapists(),fn($t)=>(int)$t['id']===$therapistId)):$this->reports->therapists()];}

    public function create(array $d,int $actorId,?int $forcedTherapistId=null):int
    {
        $n=$this->validate($d,$forcedTherapistId); $reference=$this->reports->nextReference();
        $id=$this->reports->createReport($n+['report_reference'=>$reference]);
        $this->reports->createVersion($id,1,$this->content($d),'draft',$actorId);
        $this->reports->audit($actorId,'report_created',$id,['patient_id'=>$n['patient_id'],'therapist_id'=>$n['therapist_id'],'version'=>1]); return $id;
    }

    public function update(int $id,array $d,int $actorId,?int $therapistId=null):void
    {
        $r=$this->find($id);$this->authorizeTherapist($r,$therapistId);$v=$this->reports->currentVersion($id);if($v===null)throw new RuntimeException('Report version not found.',500);
        if(in_array($r['status'],['submitted','under_review','approved'],true))throw new InvalidArgumentException('This report is locked at its current workflow stage. Create a new version after approval.');
        $this->reports->updateVersion((int)$v['id'],$this->content($d));$this->reports->audit($actorId,'report_updated',$id,['version'=>(int)$v['version_number']]);
    }

    public function submit(int $id,int $actorId,?int $therapistId=null):void
    {
        $r=$this->find($id);$this->authorizeTherapist($r,$therapistId);if(!in_array($r['status'],['draft','returned'],true))throw new InvalidArgumentException('Only draft or returned reports can be submitted.');
        $v=$this->reports->currentVersion($id);if($v===null)throw new RuntimeException('Report version not found.',500);$this->reports->setVersionStatus((int)$v['id'],'submitted');$this->reports->setReportStatus($id,'submitted','submitted_at');$this->reports->audit($actorId,'report_submitted',$id,['version'=>(int)$v['version_number']]);
    }

    public function review(int $id,int $actorId):void
    {
        $r=$this->find($id);if($r['status']!=='submitted')throw new InvalidArgumentException('Only submitted reports can be placed under review.');$v=$this->reports->currentVersion($id);if($v===null)throw new RuntimeException('Report version not found.',500);
        $this->reports->setVersionStatus((int)$v['id'],'under_review');$this->reports->setReportStatus($id,'under_review','reviewed_at');$this->reports->audit($actorId,'report_review_started',$id,['version'=>(int)$v['version_number']]);
    }

    public function returnForChanges(int $id,int $actorId):void
    {
        $r=$this->find($id);if(!in_array($r['status'],['submitted','under_review'],true))throw new InvalidArgumentException('Only submitted or under-review reports can be returned.');$v=$this->reports->currentVersion($id);if($v===null)throw new RuntimeException('Report version not found.',500);
        $this->reports->setVersionStatus((int)$v['id'],'returned');$this->reports->setReportStatus($id,'returned');$this->reports->audit($actorId,'report_returned',$id,['version'=>(int)$v['version_number']]);
    }

    public function approve(int $id,int $actorId):void
    {
        $r=$this->find($id);if($r['status']!=='under_review')throw new InvalidArgumentException('Only reports under review can be approved.');$v=$this->reports->currentVersion($id);if($v===null)throw new RuntimeException('Report version not found.',500);
        $this->reports->setVersionStatus((int)$v['id'],'approved',$actorId);$this->reports->setReportStatus($id,'approved','approved_at',$actorId);$this->reports->audit($actorId,'report_approved',$id,['version'=>(int)$v['version_number']]);
    }

    public function newVersion(int $id,int $actorId,?int $therapistId=null):int
    {
        $r=$this->find($id);$this->authorizeTherapist($r,$therapistId);if($r['status']!=='approved')throw new InvalidArgumentException('A new version can only be created from an approved report.');$current=(int)$r['current_version'];$id2=$this->reports->createVersion($id,$current+1,'','draft',$actorId);$this->reports->setCurrentVersion($id,$current+1);$this->reports->setReportStatus($id,'draft');$this->reports->audit($actorId,'report_new_version',$id,['version'=>$current+1]);return $id2;
    }

    public function canTherapistAccess(array $r,int $therapistId):bool{return (int)$r['therapist_id']===$therapistId && $this->reports->activeAssignment((int)$r['patient_id'],$therapistId)!==null;}
    private function authorizeTherapist(array $r,?int $therapistId):void{if($therapistId!==null&&!$this->canTherapistAccess($r,$therapistId))throw new RuntimeException('You do not have permission to access this report.',403);}
    private function validate(array $d,?int $forcedTherapistId=null):array{
        $patientId=(int)($d['patient_id']??0);$therapistId=$forcedTherapistId??(int)($d['therapist_id']??0);if($patientId<1)throw new InvalidArgumentException('Select a patient.');if($therapistId<1)throw new InvalidArgumentException('Select a therapist.');$assignment=$this->reports->activeAssignment($patientId,$therapistId);if($assignment===null)throw new InvalidArgumentException('The therapist must have a current active assignment to this patient.');return ['patient_id'=>$patientId,'therapist_id'=>$therapistId,'assignment_id'=>(int)$assignment['id']];
    }
    private function content(array $d):string{foreach(['summary','clinical_findings','progress','recommendations','additional_notes'] as $f){if(trim((string)($d[$f]??''))!=='')return $this->encode($d);}throw new InvalidArgumentException('Add clinical report content before saving.');}
    private function encode(array $d):string{return json_encode(['summary'=>trim((string)($d['summary']??'')),'clinical_findings'=>trim((string)($d['clinical_findings']??'')),'progress'=>trim((string)($d['progress']??'')),'recommendations'=>trim((string)($d['recommendations']??'')),'additional_notes'=>trim((string)($d['additional_notes']??''))],JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_THROW_ON_ERROR);}
}
