<?php

namespace Modules\MedWorkforce\Http\Controllers;

use Carbon\CarbonPeriod;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Modules\MedWorkforce\Entities\RosterAssignment;
use Modules\MedWorkforce\Entities\ShiftTemplate;
use Modules\MedWorkforce\Services\RosterService;

/**
 * Duty-roster board (SIFA P7-T1). Weekly calendar grid: rows = staff, columns =
 * the 7 days of the selected week, cells = shift assignments coloured by
 * template. Click a cell to assign/edit; a bulk-assign modal paints a template
 * across staff × a date range. Business-scoped; guarded by medwf.roster.*.
 */
class RosterController extends MedWorkforceBaseController
{
    public function __construct(private RosterService $roster)
    {
    }

    /** Weekly board. ?week=YYYY-MM-DD anchors the Monday; filters by dept/location. */
    public function index(Request $request)
    {
        $this->guard('medwf.roster.view');

        $anchor = $request->filled('week') ? Carbon::parse($request->get('week')) : Carbon::today();
        $monday = $anchor->copy()->startOfWeek(Carbon::MONDAY);
        $sunday = $monday->copy()->addDays(6);

        $days = collect(CarbonPeriod::create($monday, $sunday))->map(fn ($d) => $d->copy())->all();

        $departmentId = $request->filled('department_id') ? (int) $request->get('department_id') : null;
        $locationId = $request->filled('business_location_id') ? (int) $request->get('business_location_id') : null;

        $staff = $this->staff(); // id => name

        // Load the week's assignments once, keyed [user_id][Y-m-d] => [assignments].
        $query = RosterAssignment::forBusiness($this->businessId())
            ->active()
            ->between($monday->toDateString(), $sunday->toDateString())
            ->with('template');

        if ($departmentId) {
            $query->where('department_id', $departmentId);
        }
        if ($locationId) {
            $query->where('business_location_id', $locationId);
        }

        $grid = [];
        foreach ($query->get() as $a) {
            $dateKey = $a->date instanceof Carbon ? $a->date->toDateString() : (string) $a->date;
            $grid[(int) $a->user_id][$dateKey][] = $a;
        }

        return view('medworkforce::roster.index', [
            'monday'       => $monday,
            'sunday'       => $sunday,
            'days'         => $days,
            'staff'        => $staff,
            'grid'         => $grid,
            'templates'    => ShiftTemplate::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'departments'  => $this->departments(),
            'locations'    => $this->locations(),
            'departmentId' => $departmentId,
            'locationId'   => $locationId,
            'canManage'    => auth()->user()->can('medwf.roster.manage'),
            'prevWeek'     => $monday->copy()->subWeek()->toDateString(),
            'nextWeek'     => $monday->copy()->addWeek()->toDateString(),
        ]);
    }

    /** Cell editor form (assign or edit a single staff-day). */
    public function cell(Request $request)
    {
        $this->guard('medwf.roster.manage');

        $userId = (int) $request->get('user_id');
        $date = $request->get('date');
        $assignment = $request->filled('assignment_id')
            ? RosterAssignment::forBusiness($this->businessId())->find($request->get('assignment_id'))
            : null;

        return view('medworkforce::roster.cell', [
            'assignment'  => $assignment,
            'userId'      => $userId,
            'date'        => $date,
            'staff'       => $this->staff(),
            'templates'   => ShiftTemplate::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'departments' => $this->departments(),
            'locations'   => $this->locations(),
            'statuses'    => RosterAssignment::STATUSES,
        ]);
    }

    /** Save a single cell (assign/update). */
    public function store(Request $request)
    {
        $this->guard('medwf.roster.manage');

        $data = $this->validate($request, [
            'user_id'              => 'required|integer',
            'date'                 => 'required|date',
            'shift_template_id'    => 'nullable|integer',
            'department_id'        => 'nullable|integer',
            'business_location_id' => 'nullable|integer',
            'status'               => 'nullable|in:' . implode(',', RosterAssignment::STATUSES),
            'notes'                => 'nullable|string|max:500',
        ]);

        try {
            $out = $this->roster->assign(
                $this->businessId(),
                (int) $data['user_id'],
                $data['date'],
                [
                    'shift_template_id'    => $data['shift_template_id'] ?? null,
                    'department_id'        => $data['department_id'] ?? null,
                    'business_location_id' => $data['business_location_id'] ?? null,
                    'status'               => $data['status'] ?? 'scheduled',
                    'notes'                => $data['notes'] ?? null,
                    'created_by'           => $this->userId(),
                ]
            );

            $note = $this->warningNote($out['warnings']);

            return $this->ok(__('medworkforce::lang.assignment_saved'), $note ? ['note' => $note] : []);
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /** Delete (cancel) an assignment. */
    public function destroy($id)
    {
        $this->guard('medwf.roster.manage');

        try {
            $assignment = RosterAssignment::forBusiness($this->businessId())->findOrFail($id);
            $assignment->delete();

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

    /** Bulk-assign modal form (template × staff × date range). */
    public function bulkForm()
    {
        $this->guard('medwf.roster.manage');

        return view('medworkforce::roster.bulk', [
            'staff'       => $this->staff(),
            'templates'   => ShiftTemplate::forBusiness($this->businessId())->active()->orderBy('name')->get(),
            'departments' => $this->departments(),
            'locations'   => $this->locations(),
        ]);
    }

    /** Persist a bulk assignment. */
    public function bulkStore(Request $request)
    {
        $this->guard('medwf.roster.manage');

        $data = $this->validate($request, [
            'shift_template_id'    => 'required|integer',
            'user_ids'             => 'required|array|min:1',
            'user_ids.*'           => 'integer',
            'from'                 => 'required|date',
            'to'                   => 'required|date|after_or_equal:from',
            'department_id'        => 'nullable|integer',
            'business_location_id' => 'nullable|integer',
            'skip_weekends'        => 'nullable|boolean',
        ]);

        try {
            $skipDows = $request->boolean('skip_weekends') ? [6, 7] : []; // Sat, Sun

            $out = $this->roster->bulkAssign(
                $this->businessId(),
                (int) $data['shift_template_id'],
                array_map('intval', $data['user_ids']),
                $data['from'],
                $data['to'],
                [
                    'department_id'        => $data['department_id'] ?? null,
                    'business_location_id' => $data['business_location_id'] ?? null,
                    'created_by'           => $this->userId(),
                ],
                $skipDows
            );

            $note = $this->warningNote($out['warnings']);

            return $this->ok(
                __('medworkforce::lang.bulk_done', ['count' => $out['created']]),
                $note ? ['note' => $note] : []
            );
        } catch (\Exception $e) {
            \Log::emergency($e->getMessage());

            return $this->fail();
        }
    }

    /** Turn warning keys into a single human note (non-blocking). */
    private function warningNote(array $warnings): ?string
    {
        if (empty($warnings)) {
            return null;
        }

        $parts = [];
        if (in_array('double_booked', $warnings, true)) {
            $parts[] = __('medworkforce::lang.warn_double_booked');
        }
        if (in_array('min_rest', $warnings, true)) {
            $parts[] = __('medworkforce::lang.warn_min_rest');
        }
        if (in_array('template_not_found', $warnings, true)) {
            $parts[] = __('medworkforce::lang.warn_template_missing');
        }

        return $parts ? implode(' ', $parts) : null;
    }
}
