<?php

namespace Modules\MedWard\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Modules\MedWard\Entities\MedwardAdmission;
use Modules\MedWard\Entities\MedwardBed;
use Modules\MedWard\Entities\MedwardWard;
use Modules\MedWard\Services\AdmissionService;
use Modules\MedWard\Services\MarService;

/**
 * Bed board (SIFA P6-T1) — ward columns of status-coloured bed cards, refreshed
 * every ~30s. Clicking a bed opens admit / transfer / release actions. The board
 * data endpoint returns JSON so the page can poll without a reload. Guarded by
 * medward.board.view; admit/transfer additionally require medward.admission.create,
 * release requires medward.admission.discharge.
 */
class BedBoardController extends MedWardBaseController
{
    public function index()
    {
        $this->guard('medward.board.view');

        return view('medward::board.index', [
            'wards'          => MedwardWard::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'refreshSeconds' => (int) config('medward.board_refresh_seconds', 30),
        ]);
    }

    /** JSON snapshot of every ward + its beds for the polling board. */
    public function data()
    {
        $this->guard('medward.board.view');
        $businessId = $this->businessId();

        $wards = MedwardWard::forBusiness($businessId)->active()->orderBy('name')->get();
        $beds = MedwardBed::forBusiness($businessId)->orderBy('bed_number')->get()->groupBy('ward_id');

        // Preload the occupants for occupied beds.
        $admissionIds = $beds->flatten()->pluck('current_admission_id')->filter()->unique()->values();
        $admissions = MedwardAdmission::forBusiness($businessId)->whereIn('id', $admissionIds)->get()->keyBy('id');

        $payload = $wards->map(function ($ward) use ($beds, $admissions) {
            $wardBeds = ($beds->get($ward->id) ?? collect())->map(function ($bed) use ($admissions) {
                $adm = $bed->current_admission_id ? $admissions->get($bed->current_admission_id) : null;

                return [
                    'id'              => $bed->id,
                    'bed_number'      => $bed->bed_number,
                    'bed_type'        => $bed->bed_type,
                    'status'          => $bed->status,
                    'status_color'    => $bed->statusColor(),
                    'cleaning_status' => $bed->cleaning_status,
                    'admission_id'    => $adm->id ?? null,
                    'admission_no'    => $adm->admission_no ?? null,
                    'is_icu'          => (bool) ($adm->is_icu ?? false),
                    'patient_name'    => $adm ? $this->patientName((int) $adm->patient_id) : null,
                ];
            })->values();

            return [
                'id'        => $ward->id,
                'name'      => $ward->name,
                'ward_type' => $ward->ward_type,
                'beds'      => $wardBeds,
                'available' => $wardBeds->where('status', 'available')->count(),
                'occupied'  => $wardBeds->where('status', 'occupied')->count(),
                'total'     => $wardBeds->count(),
            ];
        });

        return response()->json(['wards' => $payload]);
    }

    /** Available beds in a ward (for the transfer picker). */
    public function availableBeds(Request $request)
    {
        $this->guard('medward.board.view');

        $beds = MedwardBed::forBusiness($this->businessId())
            ->where('status', 'available')
            ->when($request->filled('ward_id'), fn ($q) => $q->where('ward_id', $request->integer('ward_id')))
            ->orderBy('ward_id')->orderBy('bed_number')
            ->get(['id', 'ward_id', 'bed_number', 'bed_type']);

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

    /** Transfer an active admission to another available bed. */
    public function transfer(Request $request, AdmissionService $service)
    {
        $this->guard('medward.admission.create');
        $this->validate($request, ['admission_id' => 'required|integer', 'bed_id' => 'required|integer']);

        $admission = MedwardAdmission::forBusiness($this->businessId())->findOrFail($request->integer('admission_id'));

        try {
            $service->transfer($admission, $request->integer('bed_id'), $request->get('reason'));

            return $this->ok(__('medward::lang.transfer_done'));
        } 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();
        }
    }

    /** Mark a released (dirty) bed clean, or a maintenance/blocked bed available again. */
    public function updateBedStatus(Request $request)
    {
        $this->guard('medward.wards.manage');
        $this->validate($request, ['bed_id' => 'required|integer', 'action' => 'required|string']);

        $bed = MedwardBed::forBusiness($this->businessId())->findOrFail($request->integer('bed_id'));

        try {
            switch ($request->get('action')) {
                case 'mark_clean':
                    $bed->update(['cleaning_status' => 'clean']);
                    break;
                case 'set_maintenance':
                    if ($bed->status === 'occupied') {
                        return $this->fail(__('medward::lang.bed_occupied_locked'));
                    }
                    $bed->update(['status' => 'maintenance']);
                    break;
                case 'set_available':
                    if ($bed->status === 'occupied') {
                        return $this->fail(__('medward::lang.bed_occupied_locked'));
                    }
                    $bed->update(['status' => 'available']);
                    break;
                default:
                    return $this->fail(__('medward::lang.invalid_action'));
            }

            return $this->ok(__('medward::lang.bed_updated'));
        } catch (\Exception $e) {
            return $this->fail();
        }
    }
}
