<?php

namespace Modules\MedSurgery\Http\Controllers;

use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Modules\MedSurgery\Entities\MedsxCase;
use Modules\MedSurgery\Services\SurgeryService;

/**
 * OR scheduling board (SIFA P6-T4) — a week/day grid whose rows are OR rooms
 * (org_rooms room_type=operating) and whose blocks are cases with a scheduled
 * slot in that room. Overlapping slots in the same room are flagged (warn-only,
 * non-blocking — surfaced per case via SurgeryService::conflictsFor()).
 *
 * When MedOrganization has no operating rooms the view prompts the user to add
 * one there (MedSurgery never creates a parallel rooms table).
 */
class BoardController extends MedSurgeryBaseController
{
    public function index(Request $request, SurgeryService $service)
    {
        $this->guard('medsx.case.manage');

        $view = $request->get('view') === 'day' ? 'day' : 'week';
        $anchor = $request->filled('date')
            ? Carbon::parse($request->get('date'))->startOfDay()
            : Carbon::today();

        [$rangeStart, $rangeEnd, $days] = $this->range($view, $anchor);

        $rooms = $this->operatingRooms();

        // Cases with a scheduled slot inside the range, grouped by room then day.
        $cases = MedsxCase::forBusiness($this->businessId())
            ->whereNotNull('or_room_id')
            ->whereNotNull('scheduled_start')
            ->whereNotIn('status', ['cancelled'])
            ->whereBetween('scheduled_start', [$rangeStart, $rangeEnd])
            ->orderBy('scheduled_start')
            ->get();

        // Precompute conflict ids (warn-only).
        $conflictIds = [];
        foreach ($cases as $case) {
            if ($service->hasConflict($case)) {
                $conflictIds[$case->id] = true;
            }
        }

        $patientNames = $this->patientNames($cases->pluck('patient_id')->unique()->all());

        // Group blocks: [room_id][Y-m-d] => [cases].
        $grid = [];
        foreach ($cases as $case) {
            $day = Carbon::parse($case->scheduled_start)->format('Y-m-d');
            $grid[$case->or_room_id][$day][] = $case;
        }

        return view('medsurgery::board.index', [
            'viewMode'     => $view,
            'anchor'       => $anchor,
            'days'         => $days,
            'rooms'        => $rooms,
            'grid'         => $grid,
            'conflictIds'  => $conflictIds,
            'patientNames' => $patientNames,
            'prevDate'     => $anchor->copy()->sub($view === 'day' ? '1 day' : '1 week')->toDateString(),
            'nextDate'     => $anchor->copy()->add($view === 'day' ? '1 day' : '1 week')->toDateString(),
            'orgInstalled' => Schema::hasTable('org_rooms'),
        ]);
    }

    /** [rangeStart, rangeEnd, [Carbon days]] for the chosen view. */
    private function range(string $view, Carbon $anchor): array
    {
        if ($view === 'day') {
            $start = $anchor->copy()->startOfDay();
            $end = $anchor->copy()->endOfDay();

            return [$start, $end, [$anchor->copy()]];
        }

        $start = $anchor->copy()->startOfWeek();
        $end = $anchor->copy()->endOfWeek();
        $days = [];
        for ($d = $start->copy(); $d->lte($end); $d->addDay()) {
            $days[] = $d->copy();
        }

        return [$start, $end->endOfDay(), $days];
    }

    private function operatingRooms(): array
    {
        if (! Schema::hasTable('org_rooms')) {
            return [];
        }

        return DB::table('org_rooms')
            ->where('business_id', $this->businessId())
            ->where('room_type', config('medsurgery.or_room_type', 'operating'))
            ->orderBy('name')
            ->get(['id', 'name'])
            ->map(fn ($r) => (array) $r)
            ->toArray();
    }

    private function patientNames(array $patientIds): array
    {
        if (empty($patientIds) || ! Schema::hasTable('clc_patients')) {
            return [];
        }

        return DB::table('clc_patients')
            ->leftJoin('contacts', 'clc_patients.contact_id', '=', 'contacts.id')
            ->whereIn('clc_patients.id', $patientIds)
            ->pluck(DB::raw('COALESCE(contacts.name, clc_patients.patient_name)'), 'clc_patients.id')
            ->toArray();
    }
}
