<?php

namespace Modules\ClinicCore\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Schema;
use Modules\ClinicCore\Entities\ClcAppointment;
use Modules\ClinicCore\Entities\ClcAppointmentNote;
use Modules\ClinicCore\Entities\ClcAppointmentDiagnosis;
use Modules\ClinicCore\Entities\ClcNoteAmendment;
use Modules\ClinicCore\Entities\ClcMedicalCondition;
use Modules\ClinicCore\Entities\ClcPatientCondition;

/**
 * SIFA P3-T2 (encounters) + P3-T3 (clinical notes) endpoints.
 *
 * Everything here is business_id-scoped, permission-gated, and additive — the
 * legacy consultation save flow (ClcAppointmentController::saveConsultation) is
 * left untouched; these AJAX endpoints layer encounter diagnoses, sign/lock,
 * amendments and typed notes on top of it.
 */
class ClcEncounterController extends Controller
{
    private function businessId(): int
    {
        return (int) request()->session()->get('user.business_id');
    }

    private function appointment($id): ClcAppointment
    {
        return ClcAppointment::where('business_id', $this->businessId())->findOrFail($id);
    }

    // ─────────────────────────────────────────────────────────────────────
    // P3-T2 · Encounter meta (type / priority / check-in / discharge)
    // ─────────────────────────────────────────────────────────────────────

    /** Persist encounter_type + priority (consult screen selects). */
    public function updateMeta(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        $data = [];
        if (Schema::hasColumn('clc_appointments', 'encounter_type')) {
            $type = $request->input('encounter_type');
            $data['encounter_type'] = in_array($type, ClcAppointment::ENCOUNTER_TYPES, true) ? $type : null;
        }
        if (Schema::hasColumn('clc_appointments', 'priority')) {
            $priority = $request->input('priority');
            $data['priority'] = in_array($priority, ClcAppointment::PRIORITIES, true) ? $priority : null;
        }
        if ($data) {
            $appt->update($data);
        }

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.saved_successfully')]);
    }

    /** Check-in button — stamp check_in_time (visible pre-consult). */
    public function checkIn($id)
    {
        if (!auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        if (Schema::hasColumn('clc_appointments', 'check_in_time') && !$appt->check_in_time) {
            $appt->update(['check_in_time' => now()]);
        }

        return response()->json([
            'success'       => true,
            'msg'           => __('clinicCore::lang.checked_in'),
            'check_in_time' => optional($appt->check_in_time)->format('d M Y H:i'),
        ]);
    }

    // ─────────────────────────────────────────────────────────────────────
    // P3-T2 · Encounter diagnoses (assessment area CRUD)
    // ─────────────────────────────────────────────────────────────────────

    public function listDiagnoses($id)
    {
        if (!auth()->user()->can('clinic.view_appointments')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        return response()->json([
            'success'   => true,
            'diagnoses' => $appt->diagnoses()->get(),
        ]);
    }

    public function storeDiagnosis(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.encounter.diagnose')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        $request->validate(['description' => 'required|string|max:255']);

        $type       = $request->input('diagnosis_type');
        $confidence = $request->input('confidence');

        $diag = ClcAppointmentDiagnosis::create([
            'business_id'    => $this->businessId(),
            'appointment_id' => $appt->id,
            'patient_id'     => $appt->patient_id,
            'icd_code'       => $request->input('icd_code') ?: null,
            'description'    => trim($request->input('description')),
            'diagnosis_type' => in_array($type, ClcAppointmentDiagnosis::TYPES, true) ? $type : 'primary',
            'confidence'     => in_array($confidence, ClcAppointmentDiagnosis::CONFIDENCES, true) ? $confidence : 'confirmed',
            'diagnosed_by'   => auth()->id(),
        ]);

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.saved_successfully'), 'diagnosis' => $diag]);
    }

    public function updateDiagnosis(Request $request, $diagnosisId)
    {
        if (!auth()->user()->can('clinic.encounter.diagnose')) {
            abort(403, 'Unauthorized action.');
        }
        $diag = ClcAppointmentDiagnosis::where('business_id', $this->businessId())->findOrFail($diagnosisId);

        $request->validate(['description' => 'required|string|max:255']);

        $type       = $request->input('diagnosis_type');
        $confidence = $request->input('confidence');

        $diag->update([
            'icd_code'       => $request->input('icd_code') ?: null,
            'description'    => trim($request->input('description')),
            'diagnosis_type' => in_array($type, ClcAppointmentDiagnosis::TYPES, true) ? $type : $diag->diagnosis_type,
            'confidence'     => in_array($confidence, ClcAppointmentDiagnosis::CONFIDENCES, true) ? $confidence : $diag->confidence,
        ]);

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.updated_successfully'), 'diagnosis' => $diag]);
    }

    public function destroyDiagnosis($diagnosisId)
    {
        if (!auth()->user()->can('clinic.encounter.diagnose')) {
            abort(403, 'Unauthorized action.');
        }
        $diag = ClcAppointmentDiagnosis::where('business_id', $this->businessId())->findOrFail($diagnosisId);
        $diag->delete();

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.deleted_successfully')]);
    }

    /**
     * Promote an encounter diagnosis into the patient's longitudinal problem
     * list (clc_patient_conditions). Idempotent per condition-name + patient:
     * a matching medical-condition is found-or-created, then the patient link
     * is upserted so re-clicking never duplicates.
     */
    public function addDiagnosisToProblemList($diagnosisId)
    {
        if (!auth()->user()->can('clinic.encounter.diagnose')) {
            abort(403, 'Unauthorized action.');
        }
        $businessId = $this->businessId();
        $diag = ClcAppointmentDiagnosis::where('business_id', $businessId)->findOrFail($diagnosisId);

        $name = trim($diag->description);
        if ($name === '') {
            return response()->json(['success' => false, 'msg' => __('clinicCore::lang.no_data')], 422);
        }

        // Idempotent per condition name + business (reuse system defaults too).
        $condition = ClcMedicalCondition::whereRaw('LOWER(name) = ?', [mb_strtolower($name)])
            ->where(function ($q) use ($businessId) {
                $q->whereNull('business_id')->orWhere('business_id', $businessId);
            })
            ->first();

        if (!$condition) {
            $condition = ClcMedicalCondition::create([
                'business_id' => $businessId,
                'name'        => $name,
                'icd_code'    => $diag->icd_code ?: null,
                'status'      => 'active',
            ]);
        }

        // Map diagnosis confidence → problem-list certainty (same vocab).
        $certainty = in_array($diag->confidence, ClcPatientCondition::CERTAINTIES, true)
            ? $diag->confidence : 'confirmed';

        $link = ClcPatientCondition::updateOrCreate(
            [
                'patient_id'   => $diag->patient_id,
                'condition_id' => $condition->id,
            ],
            [
                'diagnosed_date'  => now()->toDateString(),
                'diagnosed_by'    => null,   // clc_doctors id — unknown from users context; leave null
                'appointment_id'  => $diag->appointment_id,
                'status'          => 'active',
                'certainty'       => $certainty,
                'condition_type'  => 'diagnosis',
            ]
        );

        return response()->json([
            'success'   => true,
            'msg'       => __('clinicCore::lang.added_to_problem_list'),
            'condition' => ['id' => $link->id, 'name' => $condition->name],
        ]);
    }

    // ─────────────────────────────────────────────────────────────────────
    // P3-T3 · Sign / lock + amendments
    // ─────────────────────────────────────────────────────────────────────

    /** Sign the SOAP note → stamp signed_by/at and lock the fields. */
    public function signNote($id)
    {
        if (!auth()->user()->can('clinic.note.sign')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        if (!Schema::hasColumn('clc_appointments', 'soap_locked')) {
            return response()->json(['success' => false, 'msg' => __('clinicCore::lang.no_data')], 422);
        }

        // Co-sign path: a second, different signer cosigns an already-signed note.
        if ($appt->soap_signed_by && (int) $appt->soap_signed_by !== (int) auth()->id() && !$appt->soap_cosigned_by) {
            $appt->update(['soap_cosigned_by' => auth()->id(), 'soap_cosigned_at' => now()]);
            return response()->json(['success' => true, 'msg' => __('clinicCore::lang.note_cosigned')]);
        }

        if ($appt->soap_locked) {
            return response()->json(['success' => true, 'msg' => __('clinicCore::lang.note_already_signed')]);
        }

        $appt->update([
            'soap_signed_by' => auth()->id(),
            'soap_signed_at' => now(),
            'soap_locked'    => true,
        ]);

        return response()->json([
            'success'   => true,
            'msg'       => __('clinicCore::lang.note_signed'),
            'signed_at' => optional($appt->soap_signed_at)->format('d M Y H:i'),
        ]);
    }

    /**
     * Amend a locked SOAP section: record the previous→new diff + reason, then
     * write the new text back to the appointment column. The note stays locked.
     */
    public function amendNote(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.note.amend')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        $section = $request->input('section');
        if (!in_array($section, ClcNoteAmendment::SECTIONS, true)) {
            return response()->json(['success' => false, 'msg' => __('clinicCore::lang.invalid_section')], 422);
        }

        // Map amendment section → the appointment SOAP column.
        $colMap = [
            'subjective'     => 'soap_subjective',
            'objective'      => 'soap_objective',
            'assessment'     => 'soap_assessment',
            'plan'           => 'soap_plan',
            'investigations' => 'soap_investigations',
        ];
        $column  = $colMap[$section] ?? null;
        $newText = (string) $request->input('new_text', '');
        $prev    = $column ? (string) ($appt->{$column} ?? '') : '';

        ClcNoteAmendment::create([
            'business_id'    => $this->businessId(),
            'appointment_id' => $appt->id,
            'section'        => $section,
            'previous_text'  => $prev,
            'new_text'       => $newText,
            'reason'         => $request->input('reason') ?: null,
            'amended_by'     => auth()->id(),
            'created_at'     => now(),
        ]);

        if ($column && Schema::hasColumn('clc_appointments', $column)) {
            $appt->update([$column => $newText]);
        }

        return response()->json([
            'success'    => true,
            'msg'        => __('clinicCore::lang.note_amended'),
            'amendments' => $appt->noteAmendments()->with('amender')->get()->map(fn ($a) => [
                'section'    => $a->section,
                'reason'     => $a->reason,
                'amended_by' => optional($a->amender)->name,
                'created_at' => optional($a->created_at)->format('d M Y H:i'),
            ]),
        ]);
    }

    /** Amendment history for the consult screen modal / collapse. */
    public function listAmendments($id)
    {
        if (!auth()->user()->can('clinic.view_appointments')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        $amendments = $appt->noteAmendments()->with('amender')->get()->map(fn ($a) => [
            'section'       => $a->section,
            'previous_text' => $a->previous_text,
            'new_text'      => $a->new_text,
            'reason'        => $a->reason,
            'amended_by'    => optional($a->amender)->name,
            'created_at'    => optional($a->created_at)->format('d M Y H:i'),
        ]);

        return response()->json(['success' => true, 'amendments' => $amendments]);
    }

    // ─────────────────────────────────────────────────────────────────────
    // P3-T3 · Typed clinical notes (distinct from the SOAP block)
    // ─────────────────────────────────────────────────────────────────────

    public function storeTypedNote(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_subject_notes') && !auth()->user()->can('clinic.manage_appointments')) {
            abort(403, 'Unauthorized action.');
        }
        $appt = $this->appointment($id);

        $request->validate(['notes' => 'required|string']);

        $type = $request->input('note_type');
        $data = [
            'appointment_id' => $appt->id,
            'user_id'        => auth()->id(),
            'title'          => $request->input('title') ?: null,
            'notes'          => trim($request->input('notes')),
        ];
        if (Schema::hasColumn('clc_appointment_notes', 'note_type')) {
            $data['note_type'] = in_array($type, ClcAppointmentNote::NOTE_TYPES, true) ? $type : 'other';
        }
        if (Schema::hasColumn('clc_appointment_notes', 'author_id')) {
            $data['author_id'] = auth()->id();
        }

        $note = ClcAppointmentNote::create($data);
        $note->load('author', 'user');

        return response()->json([
            'success' => true,
            'msg'     => __('clinicCore::lang.saved_successfully'),
            'note'    => [
                'id'         => $note->id,
                'title'      => $note->title,
                'note_type'  => $note->note_type ?? null,
                'notes'      => $note->notes,
                'author'     => optional($note->author ?: $note->user)->name,
                'created_at' => optional($note->created_at)->format('d M Y H:i'),
            ],
        ]);
    }
}
