<?php

namespace App\Http\Controllers\Web;

use App\Http\Controllers\Controller;
use App\Models\Shift;
use App\Support\FormTextRules;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

class ShiftController extends Controller
{
    public function index()
    {
        $companyId = Auth::user()?->company_id;
        $query = Shift::withCount('users')->orderBy('name');
        if ($companyId) {
            $query->where('company_id', $companyId);
        }
        $shifts = $query->paginate(15);

        return view('shifts.index', compact('shifts'));
    }

    public function create()
    {
        return view('shifts.create');
    }

    public function store(Request $request)
    {
        $companyId = Auth::user()?->company_id;
        $validated = $this->validatedShiftData($request);
        $validated['company_id'] = $companyId;
        $shift = Shift::create($validated);

        if ($request->expectsJson()) {
            return response()->json(['id' => $shift->id, 'name' => $shift->name], 201);
        }

        return redirect()->route('shifts.index')->with('success', 'Shift created successfully.');
    }

    public function edit(Shift $shift)
    {
        $this->authorizeShift($shift);

        return view('shifts.edit', compact('shift'));
    }

    public function update(Request $request, Shift $shift)
    {
        $this->authorizeShift($shift);
        $shift->update($this->validatedShiftData($request, $shift));

        return redirect()->route('shifts.index')->with('success', 'Shift updated successfully.');
    }

    public function destroy(Shift $shift)
    {
        $this->authorizeShift($shift);

        if ($shift->users()->exists()) {
            return redirect()->route('shifts.index')
                ->with('error', 'Cannot delete a shift that is assigned to employees.');
        }

        $shift->delete();

        return redirect()->route('shifts.index')->with('success', 'Shift deleted successfully.');
    }

    protected function authorizeShift(Shift $shift): void
    {
        $companyId = Auth::user()?->company_id;
        if ($companyId && $shift->company_id !== $companyId) {
            abort(403);
        }
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedShiftData(Request $request, ?Shift $ignore = null): array
    {
        $validated = $request->validate($this->shiftRules(), FormTextRules::messages());
        $validated['name'] = trim((string) $validated['name']);
        $validated['clock_in_time'] = $this->normalizeShiftTime($validated['clock_in_time']);
        $validated['clock_out_time'] = $this->normalizeShiftTime($validated['clock_out_time']);
        $validated['is_active'] = $request->boolean('is_active');
        $validated['is_night_shift'] = $request->boolean('is_night_shift');

        if ($validated['clock_in_time'] === $validated['clock_out_time']) {
            throw ValidationException::withMessages([
                'clock_out_time' => 'Clock out time must be different from clock in time.',
            ]);
        }

        if ($validated['clock_out_time'] < $validated['clock_in_time']) {
            $validated['is_night_shift'] = true;
        }

        if (! array_key_exists('minimum_hours_full_day', $validated)
            || $validated['minimum_hours_full_day'] === ''
            || $validated['minimum_hours_full_day'] === null) {
            $validated['minimum_hours_full_day'] = null;
        }

        $this->assertUniqueShiftNameAndTiming($validated, $ignore);

        return $validated;
    }

    /**
     * @param  array<string, mixed>  $validated
     */
    protected function assertUniqueShiftNameAndTiming(array $validated, ?Shift $ignore = null): void
    {
        $companyId = Auth::user()?->company_id;
        $query = Shift::query()
            ->when($companyId, fn ($q) => $q->where('company_id', $companyId))
            ->where('name', $validated['name'])
            ->whereTime('clock_in_time', $validated['clock_in_time'])
            ->whereTime('clock_out_time', $validated['clock_out_time']);

        if ($ignore?->id) {
            $query->where('id', '!=', $ignore->id);
        }

        if ($query->exists()) {
            throw ValidationException::withMessages([
                'name' => 'A shift with this name and timing already exists.',
            ]);
        }
    }

    protected function normalizeShiftTime(string $time): string
    {
        return Carbon::createFromFormat(
            strlen($time) === 5 ? 'H:i' : 'H:i:s',
            $time
        )->format('H:i:s');
    }

    /**
     * @return array<string, mixed>
     */
    protected function shiftRules(): array
    {
        return [
            'name' => FormTextRules::notNumericOnly(true, 191),
            'description' => FormTextRules::notNumericOnly(false, 1000),
            'clock_in_time' => 'required|date_format:H:i,H:i:s',
            'clock_out_time' => 'required|date_format:H:i,H:i:s',
            'is_night_shift' => 'boolean',
            'is_active' => 'boolean',
            'grace_period_minutes' => 'required|integer|min:0|max:120',
            'early_going_grace_minutes' => 'required|integer|min:0|max:120',
            'minimum_hours_full_day' => 'nullable|numeric|min:0|max:24',
        ];
    }
}
