<?php

namespace App\Http\Controllers;

use App\Models\Roster;
use App\Models\User;
use App\Services\ApiResponseCache;
use App\Support\RosterData;
use Carbon\CarbonImmutable;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class RosterController extends Controller
{
    public function index(Request $request): JsonResponse
    {
        $team = RosterData::normalizeTeam($request->string('team', 'designers')->toString());
        $rosterDate = RosterData::normalizeDate($request->string('roster_date', now()->toDateString())->toString(), now()->toDateString());
        $perPage = min(max((int) $request->integer('per_page', 25), 1), 100);

        $users = User::query()
            ->select('id', 'name', 'email', 'role', 'team', 'created_at')
            ->where('team', $team)
            ->latest()
            ->paginate($perPage);

        $rosters = Roster::query()
            ->whereIn('user_id', $users->getCollection()->pluck('id'))
            ->whereDate('roster_date', $rosterDate)
            ->get()
            ->keyBy('user_id');

        $users->setCollection(
            $users->getCollection()->map(
                fn (User $user) => RosterData::toEditorRow($user, $rosters->get($user->id), $rosterDate)
            )
        );

        return response()->json($users);
    }

    public function sync(Request $request, ApiResponseCache $apiResponseCache): JsonResponse
    {
        $payload = $request->validate([
            'team' => ['required', 'string', 'in:designers,qa'],
            'roster_date' => ['required', 'date'],
            'rows' => ['required', 'array'],
            'rows.*.userId' => ['required', 'integer'],
            'rows.*.holiday' => ['nullable', 'boolean'],
            'rows.*.shift1StartDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift1StartTime' => ['nullable', 'string', 'max:32'],
            'rows.*.shift1EndDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift1EndTime' => ['nullable', 'string', 'max:32'],
            'rows.*.shift2StartDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift2StartTime' => ['nullable', 'string', 'max:32'],
            'rows.*.shift2EndDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift2EndTime' => ['nullable', 'string', 'max:32'],
            'rows.*.shift3StartDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift3StartTime' => ['nullable', 'string', 'max:32'],
            'rows.*.shift3EndDate' => ['nullable', 'string', 'max:32'],
            'rows.*.shift3EndTime' => ['nullable', 'string', 'max:32'],
        ]);

        $team = RosterData::normalizeTeam($payload['team']);
        $rosterDate = RosterData::normalizeDate($payload['roster_date'], now()->toDateString());
        $userIds = collect($payload['rows'])->pluck('userId')->unique()->values();

        $users = User::query()
            ->whereIn('id', $userIds)
            ->where('team', $team)
            ->get()
            ->keyBy('id');

        if ($users->count() !== $userIds->count()) {
            throw ValidationException::withMessages([
                'rows' => 'One or more selected users do not belong to the selected team.',
            ]);
        }

        $savedRows = DB::transaction(function () use ($payload, $users, $rosterDate, $team) {
            return collect($payload['rows'])->map(function (array $row) use ($users, $rosterDate, $team) {
                $user = $users->get((int) $row['userId']);
                $attributes = RosterData::buildRosterAttributes($row, $rosterDate, $team);

                $roster = Roster::updateOrCreate(
                    [
                        'user_id' => $user->id,
                        'roster_date' => $rosterDate,
                    ],
                    $attributes
                );

                return RosterData::toEditorRow($user, $roster->fresh(), $rosterDate);
            })->values();
        });
        $apiResponseCache->invalidateGroups(['rosters']);

        return response()->json([
            'message' => 'Roster saved successfully.',
            'data' => $savedRows,
        ]);
    }

    public function copy(Request $request, ApiResponseCache $apiResponseCache): JsonResponse
    {
        $payload = $request->validate([
            'team' => ['required', 'string', 'in:designers,qa'],
            'source_date' => ['required', 'date'],
            'target_date' => ['required', 'date'],
        ]);

        $team = RosterData::normalizeTeam($payload['team']);
        $sourceDate = RosterData::normalizeDate($payload['source_date'], now()->toDateString());
        $targetDate = RosterData::normalizeDate($payload['target_date'], now()->toDateString());

        $sourceRows = Roster::query()
            ->where('team', $team)
            ->whereDate('roster_date', $sourceDate)
            ->get();

        if ($sourceRows->isEmpty()) {
            throw ValidationException::withMessages([
                'source_date' => 'No saved roster rows were found for the selected source date.',
            ]);
        }

        DB::transaction(function () use ($sourceRows, $targetDate) {
            $targetCarbon = CarbonImmutable::parse($targetDate);

            foreach ($sourceRows as $sourceRow) {
                Roster::updateOrCreate(
                    [
                        'user_id' => $sourceRow->user_id,
                        'roster_date' => $targetDate,
                    ],
                    [
                        'team' => $sourceRow->team,
                        'shift1_start' => $sourceRow->shift1_start ? $targetCarbon->setTime($sourceRow->shift1_start->hour, $sourceRow->shift1_start->minute) : null,
                        'shift1_end' => $sourceRow->shift1_end ? $targetCarbon->setTime($sourceRow->shift1_end->hour, $sourceRow->shift1_end->minute) : null,
                        'shift2_start' => $sourceRow->shift2_start ? $targetCarbon->setTime($sourceRow->shift2_start->hour, $sourceRow->shift2_start->minute) : null,
                        'shift2_end' => $sourceRow->shift2_end ? $targetCarbon->setTime($sourceRow->shift2_end->hour, $sourceRow->shift2_end->minute) : null,
                        'shift3_start' => $sourceRow->shift3_start ? $targetCarbon->setTime($sourceRow->shift3_start->hour, $sourceRow->shift3_start->minute) : null,
                        'shift3_end' => $sourceRow->shift3_end ? $targetCarbon->setTime($sourceRow->shift3_end->hour, $sourceRow->shift3_end->minute) : null,
                        'total_hours' => $sourceRow->total_hours,
                        'is_holiday' => $sourceRow->is_holiday,
                    ]
                );
            }
        });
        $apiResponseCache->invalidateGroups(['rosters']);

        return response()->json([
            'message' => "Copied {$sourceRows->count()} roster rows to {$targetDate}.",
            'copied' => $sourceRows->count(),
        ]);
    }
}
