<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreAttendanceLeaveApplicationRequest;
use App\Http\Requests\UpdateAttendanceLeaveApplicationRequest;
use App\Models\AttendanceLeaveApplication;
use App\Models\PermanentEmployee;
use App\Models\PermanentEmployeeSalaryPayment;
use App\Models\TemporaryEmployee;
use App\Support\LeaveType;
use App\Support\StaffAttendance;
use App\Support\StaffType;
use Carbon\Carbon;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response;

class AttendanceLeaveApplicationController extends Controller
{
    private const PER_PAGE_OPTIONS = [10, 25, 50];

    public function index(Request $request): Response
    {
        $perPage = $request->integer('per_page', 10);
        if (! in_array($perPage, self::PER_PAGE_OPTIONS, true)) {
            $perPage = 10;
        }

        $search = $request->string('search')->trim()->toString();
        $leaveType = $request->string('leave_type')->trim()->toString();
        $staffType = $request->string('staff_type')->trim()->toString();

        $applications = AttendanceLeaveApplication::query()
            ->when(in_array($leaveType, LeaveType::values(), true), fn ($q) => $q->where('leave_type', $leaveType))
            ->when(in_array($staffType, StaffType::values(), true), fn ($q) => $q->where('staff_type', $staffType))
            ->when($search !== '', function ($query) use ($search): void {
                $like = '%'.$search.'%';
                $permanentIds = PermanentEmployee::query()
                    ->where(function ($q) use ($like): void {
                        $q->where('full_name', 'like', $like)
                            ->orWhere('national_card_number', 'like', $like);
                    })
                    ->pluck('id');
                $temporaryIds = TemporaryEmployee::query()
                    ->where(function ($q) use ($like): void {
                        $q->where('full_name', 'like', $like)
                            ->orWhere('national_card_number', 'like', $like);
                    })
                    ->pluck('id');

                $query->where(function ($q) use ($like, $permanentIds, $temporaryIds): void {
                    $q->where('reason', 'like', $like)
                        ->orWhere(function ($q2) use ($permanentIds): void {
                            $q2->where('staff_type', StaffType::PERMANENT)
                                ->whereIn('staff_id', $permanentIds);
                        })
                        ->orWhere(function ($q2) use ($temporaryIds): void {
                            $q2->where('staff_type', StaffType::TEMPORARY)
                                ->whereIn('staff_id', $temporaryIds);
                        });
                });
            })
            ->orderByDesc('from_date')
            ->orderByDesc('id')
            ->paginate($perPage)
            ->withQueryString();

        $staffKeys = $applications->getCollection()->map(fn (AttendanceLeaveApplication $row): array => [
            'staff_type' => $row->staff_type,
            'staff_id' => $row->staff_id,
        ]);

        $directory = StaffAttendance::staffDirectory($staffKeys);

        $applications->through(function (AttendanceLeaveApplication $row) use ($directory): array {
            $staff = $directory[StaffAttendance::staffKey($row->staff_type, $row->staff_id)] ?? null;

            return $this->serializeLeaveApplication($row, $staff);
        });

        return Inertia::render('employees/attendance/leaves/index', [
            'applications' => $applications,
            'filters' => [
                'search' => $search,
                'leave_type' => in_array($leaveType, LeaveType::values(), true) ? $leaveType : '',
                'staff_type' => in_array($staffType, StaffType::values(), true) ? $staffType : '',
                'per_page' => $perPage,
            ],
            'leaveTypes' => LeaveType::values(),
        ]);
    }

    public function create(): Response
    {
        return Inertia::render('employees/attendance/leaves/create', [
            'staffOptions' => $this->staffOptions(),
            'leaveTypes' => LeaveType::values(),
        ]);
    }

    public function store(StoreAttendanceLeaveApplicationRequest $request): RedirectResponse
    {
        $validated = $request->validated();

        AttendanceLeaveApplication::query()->create($validated);

        PermanentEmployeeSalaryPayment::refreshDraftsForAttendance(
            $validated['staff_type'],
            (int) $validated['staff_id'],
        );

        return redirect()
            ->route('employees.attendance.leaves.index')
            ->with('status', __('Leave application registered.'));
    }

    public function edit(AttendanceLeaveApplication $attendanceLeaveApplication): Response
    {
        return Inertia::render('employees/attendance/leaves/edit', [
            'application' => $this->serializeLeaveApplication($attendanceLeaveApplication),
            'staffOptions' => $this->staffOptions(),
            'leaveTypes' => LeaveType::values(),
        ]);
    }

    public function update(
        UpdateAttendanceLeaveApplicationRequest $request,
        AttendanceLeaveApplication $attendanceLeaveApplication,
    ): RedirectResponse {
        $previousStaffType = $attendanceLeaveApplication->staff_type;
        $previousStaffId = $attendanceLeaveApplication->staff_id;

        $attendanceLeaveApplication->fill($request->validated());
        $attendanceLeaveApplication->save();

        PermanentEmployeeSalaryPayment::refreshDraftsForAttendance(
            $previousStaffType,
            $previousStaffId,
        );

        PermanentEmployeeSalaryPayment::refreshDraftsForAttendance(
            $attendanceLeaveApplication->staff_type,
            $attendanceLeaveApplication->staff_id,
        );

        return redirect()
            ->route('employees.attendance.leaves.index')
            ->with('status', __('Leave application updated.'));
    }

    public function destroy(
        Request $request,
        AttendanceLeaveApplication $attendanceLeaveApplication,
    ): RedirectResponse {
        PermanentEmployeeSalaryPayment::refreshDraftsForAttendance(
            $attendanceLeaveApplication->staff_type,
            $attendanceLeaveApplication->staff_id,
        );

        $attendanceLeaveApplication->delete();

        return redirect()
            ->route('employees.attendance.leaves.index', $request->only(['search', 'leave_type', 'staff_type', 'per_page']))
            ->with('status', __('Leave application removed.'));
    }

    /**
     * @param  array{staff_name: string, national_card_number: string|null}|null  $staff
     * @return array<string, mixed>
     */
    private function serializeLeaveApplication(
        AttendanceLeaveApplication $row,
        ?array $staff = null,
    ): array {
        if ($staff === null) {
            $resolved = StaffAttendance::resolveStaff($row->staff_type, $row->staff_id);
            $staff = $resolved;
        }

        $from = $row->from_date;
        $to = $row->to_date;
        $totalDays = null;

        if ($from && $to) {
            $totalDays = Carbon::parse($from)->diffInDays(Carbon::parse($to)) + 1;
        }

        return [
            'id' => $row->id,
            'staff_type' => $row->staff_type,
            'staff_id' => $row->staff_id,
            'staff_name' => $staff['staff_name'] ?? __('Unknown'),
            'national_card_number' => $staff['national_card_number'] ?? null,
            'leave_type' => $row->leave_type,
            'from_date' => $from?->toDateString(),
            'to_date' => $to?->toDateString(),
            'total_days' => $totalDays,
            'reason' => $row->reason ?? '',
        ];
    }

    /**
     * @return list<array{staff_type: string, staff_id: int, label: string}>
     */
    private function staffOptions(): array
    {
        $options = [];

        foreach (PermanentEmployee::query()
            ->orderBy('full_name')
            ->get(['id', 'full_name', 'national_card_number']) as $employee) {
            $options[] = [
                'staff_type' => StaffType::PERMANENT,
                'staff_id' => $employee->id,
                'label' => $employee->full_name.' — '.$employee->national_card_number.' ('.__('Permanent').')',
            ];
        }

        foreach (TemporaryEmployee::query()
            ->orderBy('full_name')
            ->get(['id', 'full_name', 'national_card_number']) as $employee) {
            $options[] = [
                'staff_type' => StaffType::TEMPORARY,
                'staff_id' => $employee->id,
                'label' => $employee->full_name.' — '.$employee->national_card_number.' ('.__('Temporary').')',
            ];
        }

        return $options;
    }
}
