<?php

declare(strict_types=1);

namespace App\Services;

use App\Repositories\AssignmentRepository;
use App\Repositories\PatientRepository;
use InvalidArgumentException;
use RuntimeException;

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

    public function forPatient(int $patientId): array { return $this->assignments->forPatient($patientId); }
    public function availableTherapists(): array { return $this->assignments->availableTherapists(); }

    public function assign(int $patientId, int $therapistId, string $role, string $assignedAt, string $notes, int $actorId): int
    {
        $this->patient($patientId);
        if (!in_array($role, ['primary', 'supporting'], true)) throw new InvalidArgumentException('Select a valid assignment role.');
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $assignedAt)) throw new InvalidArgumentException('Enter a valid assignment start date.');
        if ($this->assignments->hasActiveAssignment($patientId, $therapistId)) throw new InvalidArgumentException('This therapist is already actively assigned to the patient.');

        $id = $this->assignments->create([
            'patient_id' => $patientId,
            'therapist_id' => $therapistId,
            'role' => $role,
            'assigned_at' => $assignedAt,
            'notes' => $notes !== '' ? $notes : null,
            'assigned_by' => $actorId,
        ]);
        $this->assignments->audit($actorId, 'patient_assigned', $id, ['patient_id'=>$patientId,'therapist_id'=>$therapistId,'role'=>$role]);
        return $id;
    }

    public function end(int $assignmentId, string $endedAt, int $actorId): void
    {
        $assignment = $this->assignments->find($assignmentId);
        if ($assignment === null) throw new RuntimeException('Assignment not found.', 404);
        if ($assignment['status'] !== 'active' || $assignment['ended_at'] !== null) throw new InvalidArgumentException('This assignment has already ended.');
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $endedAt)) throw new InvalidArgumentException('Enter a valid assignment end date.');
        if ($endedAt < $assignment['assigned_at']) throw new InvalidArgumentException('End date cannot be before the start date.');
        $this->assignments->end($assignmentId, $endedAt);
        $this->assignments->audit($actorId, 'patient_assignment_ended', $assignmentId, ['patient_id'=>$assignment['patient_id'],'therapist_id'=>$assignment['therapist_id']]);
    }

    public function canTherapistAccessPatient(int $userId, int $patientId): bool
    {
        $therapistId = $this->assignments->therapistIdForUser($userId);
        return $therapistId !== null && $this->assignments->hasActiveAssignment($patientId, $therapistId);
    }

    public function therapistIdForUser(int $userId): ?int { return $this->assignments->therapistIdForUser($userId); }

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