<?php

namespace Modules\MedEmergency\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedEmergency\Entities\MderVisit;
use Modules\MedEmergency\Entities\MderVisitEvent;

/**
 * The live ER board (SIFA P6-T3) — columns by board_status, triage-level colour
 * coding and a ticking time-in-department. Cards move between columns via
 * buttons (drag-less). Includes the today metrics strip, a LWBS quick action and
 * the visit detail drawer (timeline + triage + vitals + disposition).
 * Guarded by mder.board.view.
 */
class BoardController extends MedEmergencyBaseController
{
    /** Render the board (open visits grouped into columns) + today metrics. */
    public function index()
    {
        $this->guard('mder.board.view');

        return view('medemergency::board.index', [
            'columns'         => $this->boardColumns(),
            'metrics'         => $this->todayMetrics(),
            'boardStatuses'   => MderVisit::BOARD_STATUSES,
            'triageColors'    => MderVisit::TRIAGE_COLORS,
            'refreshSeconds'  => (int) config('medemergency.board_refresh_seconds', 30),
        ]);
    }

    /** Board body only — used by the 30s auto-refresh poll (returns a partial). */
    public function refresh()
    {
        $this->guard('mder.board.view');

        return view('medemergency::board.columns', [
            'columns'        => $this->boardColumns(),
            'boardStatuses'  => MderVisit::BOARD_STATUSES,
            'triageColors'   => MderVisit::TRIAGE_COLORS,
        ]);
    }

    /** Move a visit to another board_status (drag-less button action). */
    public function move(Request $request, $id)
    {
        $this->guard('mder.board.view');

        $data = $this->validate($request, [
            'board_status' => 'required|string|in:' . implode(',', MderVisit::BOARD_STATUSES),
        ]);

        $visit = MderVisit::forBusiness($this->businessId())->findOrFail($id);

        // Guard: disposed visits are frozen on the board.
        if ($visit->isClosed()) {
            return $this->fail(__('medemergency::lang.visit_closed'));
        }

        $from = $visit->board_status;
        $visit->board_status = $data['board_status'];
        $visit->save();

        // A move into in_progress is the "seen by provider" milestone (drives the
        // door-to-doctor metric); anything else is a plain board note.
        $eventType = $data['board_status'] === 'in_progress' ? 'seen' : 'note';
        MderVisitEvent::log(
            $visit->id,
            $eventType,
            __('medemergency::lang.moved_board', [
                'from' => $this->statusLabel($from),
                'to'   => $this->statusLabel($data['board_status']),
            ])
        );

        return $this->ok(__('medemergency::lang.board_updated'), ['board_status' => $visit->board_status]);
    }

    /** LWBS quick action — mark left-without-being-seen and close the visit. */
    public function lwbs(Request $request, $id)
    {
        $this->guard('mder.board.view');

        $visit = MderVisit::forBusiness($this->businessId())->findOrFail($id);

        if ($visit->isClosed()) {
            return $this->fail(__('medemergency::lang.visit_closed'));
        }

        $visit->update([
            'disposition'      => 'lwbs',
            'disposition_at'   => now(),
            'disposition_note' => $request->input('note'),
            'board_status'     => 'done',
        ]);

        MderVisitEvent::log($visit->id, 'disposition', __('medemergency::lang.disp_lwbs'));

        return $this->ok(__('medemergency::lang.lwbs_recorded'));
    }

    /** Visit detail drawer: timeline + triage + vitals + disposition. */
    public function show($id)
    {
        $this->guard('mder.board.view');

        $visit = MderVisit::forBusiness($this->businessId())
            ->with('events')
            ->findOrFail($id);

        // Load the triage vitals row (ClinicCore) if one was written.
        $vitals = null;
        if ($visit->triage_vitals_id && Schema::hasTable('clc_vitals')) {
            $vitals = DB::table('clc_vitals')->where('id', $visit->triage_vitals_id)->first();
        }

        // Resolve the assigned doctor name (fail-soft).
        $doctorName = null;
        if ($visit->assigned_doctor_id && Schema::hasTable('clc_doctors')) {
            $doctorName = DB::table('clc_doctors')->where('id', $visit->assigned_doctor_id)->value('name');
        }

        return view('medemergency::board.detail', [
            'visit'       => $visit,
            'vitals'      => $vitals,
            'doctorName'  => $doctorName,
            'dispositions' => MderVisit::DISPOSITIONS,
            'triageColors' => MderVisit::TRIAGE_COLORS,
        ]);
    }

    /** Open visits grouped by board_status, resuscitation/emergent first. */
    private function boardColumns(): array
    {
        $visits = MderVisit::forBusiness($this->businessId())
            ->whereNotIn('board_status', ['done'])
            ->orderByRaw("FIELD(triage_level,'resuscitation','emergent','urgent','less_urgent','non_urgent')")
            ->orderBy('arrival_at')
            ->get();

        $columns = [];
        foreach (MderVisit::BOARD_STATUSES as $status) {
            if ($status === 'done') {
                continue; // done visits leave the board
            }
            $columns[$status] = $visits->where('board_status', $status)->values();
        }

        return $columns;
    }

    /**
     * Today's ER metrics strip (simple queries):
     *   - door-to-triage median (arrival → triaged)
     *   - door-to-doctor median (arrival → first in_progress / seen)
     *   - LWBS count
     */
    private function todayMetrics(): array
    {
        $businessId = $this->businessId();
        $today = now()->startOfDay();

        $todays = MderVisit::forBusiness($businessId)
            ->whereDate('arrival_at', $today)
            ->get(['id', 'arrival_at', 'triaged_at', 'disposition']);

        // door-to-triage minutes.
        $doorToTriage = $todays
            ->filter(fn ($v) => $v->arrival_at && $v->triaged_at)
            ->map(fn ($v) => $v->arrival_at->diffInMinutes($v->triaged_at))
            ->values();

        // door-to-doctor minutes: arrival → first "seen" / in_progress event.
        $seenTimes = MderVisitEvent::whereIn('visit_id', $todays->pluck('id'))
            ->where('event', 'seen')
            ->orderBy('id')
            ->get(['visit_id', 'created_at'])
            ->groupBy('visit_id')
            ->map(fn ($rows) => $rows->first()->created_at);

        $arrivalById = $todays->keyBy('id');
        $doorToDoctor = collect();
        foreach ($seenTimes as $visitId => $seenAt) {
            $arrival = optional($arrivalById->get($visitId))->arrival_at;
            if ($arrival && $seenAt) {
                $doorToDoctor->push($arrival->diffInMinutes($seenAt));
            }
        }

        return [
            'door_to_triage'  => $this->median($doorToTriage),
            'door_to_doctor'  => $this->median($doorToDoctor),
            'lwbs_count'      => $todays->where('disposition', 'lwbs')->count(),
            'arrivals_count'  => $todays->count(),
        ];
    }

    /** Median of a numeric collection, or null when empty. */
    private function median($values): ?int
    {
        $values = collect($values)->filter(fn ($v) => $v !== null)->sort()->values();
        $n = $values->count();
        if ($n === 0) {
            return null;
        }
        $mid = intdiv($n, 2);

        return $n % 2
            ? (int) round($values[$mid])
            : (int) round(($values[$mid - 1] + $values[$mid]) / 2);
    }

    private function statusLabel(string $status): string
    {
        return __('medemergency::lang.board_' . $status);
    }
}
