<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Api\Concerns\ScopesApiToAccount;
use App\Http\Controllers\Controller;
use App\Models\Assessment;
use App\Models\AssessmentTemplate;
use App\Models\Consultation;
use App\Models\Patient;
use App\Models\Visit;
use App\Services\Care\AssessmentService;
use App\Services\Care\CareFeatures;
use App\Services\Care\CarePermissions;
use App\Services\Care\FhirAssessmentExporter;
use App\Services\Care\OrganizationResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class AssessmentController extends Controller
{
    use ScopesApiToAccount;

    public function __construct(
        protected AssessmentService $assessments,
        protected CareFeatures $features,
        protected CarePermissions $permissions,
        protected FhirAssessmentExporter $fhir,
    ) {}

    public function index(Request $request, Patient $patient): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeAbility($request, 'assessments.view');
        $this->authorizePatient($request, $patient);

        $branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));
        $page = $this->assessments->listForPatient(
            $patient,
            $this->ownerRef($request),
            $request->only(['status', 'template_code', 'category', 'from', 'to', 'per_page']),
            $branchScope,
        );

        return response()->json($page);
    }

    public function templates(Request $request): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeAbility($request, 'assessments.view');

        $templates = AssessmentTemplate::query()
            ->system()
            ->current()
            ->with('questions')
            ->orderBy('category')
            ->orderBy('name')
            ->get();

        return response()->json(['data' => $templates]);
    }

    public function store(Request $request, Patient $patient): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeCapture($request);
        $this->authorizePatient($request, $patient);

        $validated = $request->validate([
            'template_code' => ['required', 'string', 'max:100'],
            'consultation_uuid' => ['nullable', 'uuid'],
            'visit_uuid' => ['nullable', 'uuid'],
        ]);

        $context = $this->resolveContext($request, $patient, $validated);
        $assessment = $this->assessments->start(
            $patient,
            $validated['template_code'],
            $this->ownerRef($request),
            $this->member($request),
            $context,
        );

        $status = $assessment->wasRecentlyCreated ? 201 : 200;

        return response()->json($this->serializeAssessment($assessment), $status);
    }

    public function storeForConsultation(Request $request, Consultation $consultation): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeCapture($request);
        $this->authorizeConsultation($request, $consultation);
        $consultation->loadMissing(['patient', 'visit']);
        $this->authorizePatient($request, $consultation->patient);

        $validated = $request->validate([
            'template_code' => ['required', 'string', 'max:100'],
        ]);

        $assessment = $this->assessments->start(
            $consultation->patient,
            $validated['template_code'],
            $this->ownerRef($request),
            $this->member($request),
            [
                'consultation' => $consultation,
                'visit' => $consultation->visit,
                'actor' => $this->ownerRef($request),
            ],
        );

        return response()->json($this->serializeAssessment($assessment), $assessment->wasRecentlyCreated ? 201 : 200);
    }

    public function show(Request $request, Assessment $assessment): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeAbility($request, 'assessments.view');
        $this->authorizeAssessment($request, $assessment);

        return response()->json($this->serializeAssessment(
            $assessment->load(['template.questions', 'answers.question', 'score', 'patient', 'consultation', 'visit'])
        ));
    }

    public function update(Request $request, Assessment $assessment): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeCapture($request);
        $this->authorizeAssessment($request, $assessment);

        $validated = $request->validate([
            'answers' => ['nullable', 'array'],
            'notes' => ['nullable', 'string', 'max:10000'],
        ]);

        $updated = $this->assessments->saveAnswers(
            $assessment,
            $this->ownerRef($request),
            $validated['answers'] ?? [],
            $this->member($request),
            $this->ownerRef($request),
            array_key_exists('notes', $validated) ? $validated['notes'] : null,
        );

        return response()->json($this->serializeAssessment($updated));
    }

    public function complete(Request $request, Assessment $assessment): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeCapture($request);
        $this->authorizeAssessment($request, $assessment);

        if ($request->has('answers') || $request->has('notes')) {
            $validated = $request->validate([
                'answers' => ['nullable', 'array'],
                'notes' => ['nullable', 'string', 'max:10000'],
            ]);
            $this->assessments->saveAnswers(
                $assessment,
                $this->ownerRef($request),
                $validated['answers'] ?? [],
                $this->member($request),
                $this->ownerRef($request),
                array_key_exists('notes', $validated) ? $validated['notes'] : null,
            );
            $assessment->refresh();
        }

        $completed = $this->assessments->complete(
            $assessment,
            $this->ownerRef($request),
            $this->member($request),
            $this->ownerRef($request),
        );

        return response()->json($this->serializeAssessment($completed));
    }

    public function cancel(Request $request, Assessment $assessment): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeCapture($request);
        $this->authorizeAssessment($request, $assessment);

        $cancelled = $this->assessments->cancel(
            $assessment,
            $this->ownerRef($request),
            $this->member($request),
            $this->ownerRef($request),
        );

        return response()->json($this->serializeAssessment($cancelled));
    }

    public function fhir(Request $request, Assessment $assessment): JsonResponse
    {
        $this->assertEngineEnabled($request);
        $this->authorizeAbility($request, 'assessments.view');
        $this->authorizeAssessment($request, $assessment);

        return response()->json(
            $this->fhir->exportBundle($assessment),
            200,
            ['Content-Type' => 'application/fhir+json'],
        );
    }

    protected function serializeAssessment(Assessment $assessment): array
    {
        $assessment->loadMissing(['template', 'answers.question', 'score', 'patient']);

        $answers = [];
        foreach ($assessment->answers as $answer) {
            if ($answer->question) {
                $answers[$answer->question->code] = $answer->authoritativeValue($answer->question->answer_type);
            }
        }

        return [
            'uuid' => $assessment->uuid,
            'status' => $assessment->status,
            'template_code' => $assessment->template?->code,
            'template_name' => $assessment->template?->name,
            'template_version' => $assessment->template?->version,
            'patient_uuid' => $assessment->patient?->uuid,
            'consultation_uuid' => $assessment->consultation?->uuid,
            'visit_uuid' => $assessment->visit?->uuid,
            'assessed_at' => $assessment->assessed_at?->toIso8601String(),
            'completed_at' => $assessment->completed_at?->toIso8601String(),
            'notes' => $assessment->notes,
            'answers' => $answers,
            'score' => $assessment->score ? [
                'total_score' => $assessment->score->total_score,
                'max_score' => $assessment->score->max_score,
                'subscores' => $assessment->score->subscores,
                'severity_label' => $assessment->score->severity_label,
                'computed_at' => $assessment->score->computed_at?->toIso8601String(),
            ] : null,
            'template' => $assessment->template,
        ];
    }

    protected function assertEngineEnabled(Request $request): void
    {
        abort_unless(
            $this->features->enabled($this->organization($request), CareFeatures::ASSESSMENTS_ENGINE),
            404,
        );
        $this->features->ensureAssessmentCatalog();
    }

    protected function authorizeCapture(Request $request): void
    {
        $member = $this->member($request);
        abort_unless(
            $this->permissions->can($member, 'assessments.capture')
            || $this->permissions->can($member, 'assessments.manage'),
            403,
        );
    }

    protected function authorizePatient(Request $request, Patient $patient): void
    {
        $this->authorizeOwner($request, $patient);
        abort_unless($patient->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId !== null && $patient->branch_id !== $branchId) {
            abort(404);
        }
    }

    protected function authorizeConsultation(Request $request, Consultation $consultation): void
    {
        $this->authorizeOwner($request, $consultation);
        $consultation->loadMissing('visit');
        abort_unless($consultation->visit->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId !== null && $consultation->visit->branch_id !== $branchId) {
            abort(404);
        }
    }

    protected function authorizeAssessment(Request $request, Assessment $assessment): void
    {
        $this->authorizeOwner($request, $assessment);
        abort_unless($assessment->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId !== null && $assessment->branch_id !== $branchId) {
            abort(404);
        }
    }

    /**
     * @param  array{consultation_uuid?: ?string, visit_uuid?: ?string}  $validated
     * @return array{consultation?: Consultation, visit?: Visit, actor: string}
     */
    protected function resolveContext(Request $request, Patient $patient, array $validated): array
    {
        $context = ['actor' => $this->ownerRef($request)];

        if (! empty($validated['consultation_uuid'])) {
            $consultation = Consultation::query()
                ->where('uuid', $validated['consultation_uuid'])
                ->where('patient_id', $patient->id)
                ->firstOrFail();
            $this->authorizeConsultation($request, $consultation);
            $consultation->loadMissing('visit');
            $context['consultation'] = $consultation;
            $context['visit'] = $consultation->visit;
        } elseif (! empty($validated['visit_uuid'])) {
            $visit = Visit::query()
                ->where('uuid', $validated['visit_uuid'])
                ->where('patient_id', $patient->id)
                ->firstOrFail();
            $this->authorizeOwner($request, $visit);
            abort_unless($visit->organization_id === $this->organization($request)->id, 404);
            $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
            if ($branchId !== null && $visit->branch_id !== $branchId) {
                abort(404);
            }
            $context['visit'] = $visit;
        }

        return $context;
    }
}
