<?php

namespace Modules\MedWard\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use Modules\MedWard\Entities\MedwardAdmission;
use Modules\MedWard\Entities\MedwardBed;
use Modules\MedWard\Entities\MedwardFluidBalance;
use Modules\MedWard\Entities\MedwardMarEntry;
use Modules\MedWard\Entities\MedwardWard;
use Modules\MedWard\Services\AdmissionService;
use Modules\MedWard\Services\MarService;
use Yajra\DataTables\Facades\DataTables;

/**
 * Admissions (SIFA P6-T2) — the list, the admit form, the admission detail with
 * tabs (overview / MAR / fluid balance / ICU flow-sheet when is_icu / discharge)
 * and the MAR + fluid-balance + discharge write actions. Guarded by the
 * medward.admission.* / medward.mar.* permissions.
 */
class AdmissionController extends MedWardBaseController
{
    public function index(Request $request)
    {
        $this->guard('medward.board.view');

        if ($request->ajax()) {
            $query = MedwardAdmission::forBusiness($this->businessId())
                ->leftJoin('medward_wards', 'medward_admissions.ward_id', '=', 'medward_wards.id')
                ->leftJoin('medward_beds', 'medward_admissions.bed_id', '=', 'medward_beds.id')
                ->select([
                    'medward_admissions.id', 'medward_admissions.admission_no', 'medward_admissions.patient_id',
                    'medward_admissions.status', 'medward_admissions.is_icu', 'medward_admissions.admitted_at',
                    'medward_wards.name as ward_name', 'medward_beds.bed_number',
                ]);

            if ($request->filled('status')) {
                $query->where('medward_admissions.status', $request->get('status'));
            }
            if ($request->boolean('icu_only')) {
                $query->where('medward_admissions.is_icu', true);
            }

            return DataTables::of($query)
                ->addColumn('patient_name', fn ($r) => $this->patientName((int) $r->patient_id))
                ->editColumn('is_icu', fn ($r) => $r->is_icu ? '<span class="label label-danger">ICU</span>' : '')
                ->editColumn('status', fn ($r) => '<span class="label ' . $this->statusClass($r->status) . '">' . __('medward::lang.adm_status_' . $r->status) . '</span>')
                ->editColumn('admitted_at', fn ($r) => optional($r->admitted_at ? \Carbon\Carbon::parse($r->admitted_at) : null)->format('Y-m-d H:i'))
                ->addColumn('action', fn ($r) => '<a class="btn btn-xs btn-primary" href="' . route('medward.admissions.show', $r->id) . '"><i class="fa fa-eye"></i> ' . __('medward::lang.open') . '</a>')
                ->rawColumns(['is_icu', 'status', 'action'])
                ->make(true);
        }

        return view('medward::admissions.index', ['statuses' => MedwardAdmission::STATUSES]);
    }

    /** Admit form — pick patient, ward, available bed. */
    public function create()
    {
        $this->guard('medward.admission.create');

        return view('medward::admissions.create', [
            'wards'      => MedwardWard::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'patients'   => $this->patientOptions(),
            'types'      => MedwardAdmission::ADMISSION_TYPES,
            'sources'    => MedwardAdmission::ADMISSION_SOURCES,
            'icuTypes'   => MedwardAdmission::ICU_TYPES,
        ]);
    }

    public function store(Request $request, AdmissionService $service)
    {
        $this->guard('medward.admission.create');

        $data = $this->validate($request, [
            'patient_id'          => 'required|integer',
            'ward_id'             => 'required|integer',
            'bed_id'              => 'required|integer',
            'admission_type'      => 'required|in:' . implode(',', MedwardAdmission::ADMISSION_TYPES),
            'admission_source'    => 'required|in:' . implode(',', MedwardAdmission::ADMISSION_SOURCES),
            'admitting_diagnosis' => 'nullable|string',
            'admitting_doctor_id' => 'nullable|integer',
            'appointment_id'      => 'nullable|integer',
            'expected_discharge'  => 'nullable|date',
            'is_icu'              => 'nullable|boolean',
            'icu_type'            => 'nullable|in:' . implode(',', MedwardAdmission::ICU_TYPES),
            'apache_ii'           => 'nullable|integer|min:0|max:71',
            'sofa'                => 'nullable|integer|min:0|max:24',
            'ventilator'          => 'nullable|boolean',
            'ventilator_mode'     => 'nullable|string|max:30',
        ]);

        try {
            $contactId = $this->contactIdForPatient((int) $data['patient_id']);
            if (! $contactId) {
                return $this->fail(__('medward::lang.patient_no_contact'));
            }

            $admission = $service->admit([
                'business_id'         => $this->businessId(),
                'patient_id'          => (int) $data['patient_id'],
                'contact_id'          => $contactId,
                'ward_id'             => (int) $data['ward_id'],
                'bed_id'              => (int) $data['bed_id'],
                'admission_type'      => $data['admission_type'],
                'admission_source'    => $data['admission_source'],
                'admitting_diagnosis' => $data['admitting_diagnosis'] ?? null,
                'admitting_doctor_id' => $data['admitting_doctor_id'] ?? null,
                'appointment_id'      => $data['appointment_id'] ?? null,
                'expected_discharge'  => $data['expected_discharge'] ?? null,
                'created_by'          => $this->userId(),
                'is_icu'              => $request->boolean('is_icu'),
                'icu_type'            => $request->boolean('is_icu') ? ($data['icu_type'] ?? null) : null,
                'apache_ii'           => $data['apache_ii'] ?? null,
                'sofa'                => $data['sofa'] ?? null,
                'ventilator'          => $request->boolean('ventilator'),
                'ventilator_mode'     => $data['ventilator_mode'] ?? null,
            ]);

            return $this->ok(__('medward::lang.admitted'), ['redirect' => route('medward.admissions.show', $admission->id)]);
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /** Admission detail with tabs. */
    public function show($id)
    {
        $this->guard('medward.board.view');

        $admission = MedwardAdmission::forBusiness($this->businessId())
            ->with(['ward', 'bed'])
            ->findOrFail($id);

        return view('medward::admissions.show', [
            'admission'     => $admission,
            'patientName'   => $this->patientName((int) $admission->patient_id),
            'marEntries'    => $admission->marEntries()->orderBy('scheduled_at')->get(),
            'fluidRows'     => $admission->fluidBalances()->orderByDesc('recorded_at')->get(),
            'flowsheets'    => $admission->flowsheets()->orderByDesc('recorded_at')->limit(20)->get(),
            'wards'         => MedwardWard::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'dischargeTypes' => MedwardAdmission::DISCHARGE_TYPES,
        ]);
    }

    // ── MAR actions ──────────────────────────────────────────────────────

    /** Generate scheduled MAR rows from active inpatient Rx items for a day. */
    public function generateMar(Request $request, $id, MarService $service)
    {
        $this->guard('medward.mar.administer');
        $admission = MedwardAdmission::forBusiness($this->businessId())->findOrFail($id);

        try {
            $day = $request->filled('day') ? \Carbon\Carbon::parse($request->get('day')) : null;
            $count = $service->generateForAdmission($admission, $day);

            return $this->ok(__('medward::lang.mar_generated', ['count' => $count]));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    public function administerMar(Request $request, $marId, MarService $service)
    {
        $this->guard('medward.mar.administer');
        $entry = $this->marEntry($marId);

        try {
            $service->administer($entry, [
                'administered_by' => $this->userId(),
                'witness_user_id' => $request->get('witness_user_id'),
                'dose'            => $request->get('dose'),
                'route'           => $request->get('route'),
            ]);

            return $this->ok(__('medward::lang.mar_given'));
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            return $this->fail();
        }
    }

    public function holdMar(Request $request, $marId, MarService $service)
    {
        $this->guard('medward.mar.administer');
        $entry = $this->marEntry($marId);

        try {
            $service->hold($entry, (string) $request->get('status', 'held'), (string) $request->get('reason', ''));

            return $this->ok(__('medward::lang.mar_updated'));
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            return $this->fail();
        }
    }

    // ── Fluid balance ────────────────────────────────────────────────────

    public function storeFluid(Request $request, $id)
    {
        $this->guard('medward.mar.administer');
        $admission = MedwardAdmission::forBusiness($this->businessId())->findOrFail($id);

        $data = $this->validate($request, [
            'shift'        => 'required|in:' . implode(',', MedwardFluidBalance::SHIFTS),
            'intake_oral'  => 'nullable|numeric|min:0',
            'intake_iv'    => 'nullable|numeric|min:0',
            'intake_other' => 'nullable|numeric|min:0',
            'output_urine' => 'nullable|numeric|min:0',
            'output_drain' => 'nullable|numeric|min:0',
            'output_other' => 'nullable|numeric|min:0',
        ]);

        try {
            MedwardFluidBalance::create(array_merge($data, [
                'business_id'  => $this->businessId(),
                'admission_id' => $admission->id,
                'recorded_at'  => now(),
                'recorded_by'  => $this->userId(),
            ]));

            return $this->ok(__('medward::lang.fluid_saved'));
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    // ── Discharge workflow ───────────────────────────────────────────────

    public function requestDischarge($id, AdmissionService $service)
    {
        $this->guard('medward.admission.discharge');
        $admission = MedwardAdmission::forBusiness($this->businessId())->findOrFail($id);

        try {
            $service->requestDischarge($admission);

            return $this->ok(__('medward::lang.discharge_pending_set'));
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            return $this->fail();
        }
    }

    public function discharge(Request $request, $id, AdmissionService $service)
    {
        $this->guard('medward.admission.discharge');
        $admission = MedwardAdmission::forBusiness($this->businessId())->findOrFail($id);

        $data = $this->validate($request, [
            'discharge_type'         => 'required|in:' . implode(',', MedwardAdmission::DISCHARGE_TYPES),
            'discharge_summary'      => 'required|string',
            'discharge_instructions' => 'nullable|string',
            'med_reconciliation'     => 'nullable|string',
            'followup_date'          => 'nullable|date',
        ]);

        try {
            $out = $service->discharge($admission, $data);

            return $this->ok(__('medward::lang.discharged'), ['bed_notified' => $out['bed_notified']]);
        } catch (ValidationException $e) {
            return response()->json(['success' => false, 'msg' => collect($e->errors())->flatten()->first()], 422);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    // ── helpers ──────────────────────────────────────────────────────────

    private function marEntry($marId): MedwardMarEntry
    {
        return MedwardMarEntry::forBusiness($this->businessId())->findOrFail($marId);
    }

    private function statusClass(string $status): string
    {
        return [
            'admitted'          => 'label-info',
            'discharge_pending' => 'label-warning',
            'discharged'        => 'label-default',
        ][$status] ?? 'label-default';
    }

    /** Patient dropdown options (fail-soft if ClinicCore absent). */
    private function patientOptions(): array
    {
        try {
            if (! Schema::hasTable('clc_patients')) {
                return [];
            }

            return DB::table('clc_patients')
                ->leftJoin('contacts', 'clc_patients.contact_id', '=', 'contacts.id')
                ->where('clc_patients.business_id', $this->businessId())
                ->orderBy('contacts.name')
                ->limit(500)
                ->pluck(DB::raw('COALESCE(contacts.name, clc_patients.patient_name)'), 'clc_patients.id')
                ->toArray();
        } catch (\Throwable $e) {
            return [];
        }
    }

    private function contactIdForPatient(int $patientId): ?int
    {
        try {
            if (! Schema::hasTable('clc_patients')) {
                return null;
            }
            $cid = DB::table('clc_patients')->where('id', $patientId)
                ->where('business_id', $this->businessId())->value('contact_id');

            return $cid ? (int) $cid : null;
        } catch (\Throwable $e) {
            return null;
        }
    }
}
