<?php

namespace App\Console\Commands;

use App\Enums\PayrollAdjustmentType;
use App\Models\Attendance;
use App\Models\Employee;
use App\Models\LeaveRequest;
use App\Models\Payroll;
use App\Models\PayrollPeriod;
use App\Settings\HrSettings;
use Carbon\Carbon;
use Illuminate\Console\Command;

class ApplyAttendancePenalties extends Command
{
    protected $signature = 'attendance:apply-penalties {--date= : Tanggal yang diproses (default: kemarin)}';

    protected $description = 'Buat potongan gaji otomatis untuk keterlambatan dan bolos';

    public function handle(): int
    {
        $date = $this->option('date')
            ? Carbon::parse($this->option('date'))->startOfDay()
            : Carbon::yesterday()->startOfDay();

        $settings = app(HrSettings::class);
        $latePenalty = $settings->late_penalty_amount;
        $absentPenalty = $settings->absent_penalty_amount;
        $scheduledTime = $settings->scheduled_check_in_time;

        if ($latePenalty <= 0 && $absentPenalty <= 0) {
            $this->info('Denda keterlambatan dan bolos belum diatur (0). Lewatkan.');

            return self::SUCCESS;
        }

        $scheduledCheckIn = Carbon::createFromFormat('Y-m-d H:i', $date->format('Y-m-d').' '.$scheduledTime);

        $employees = Employee::query()
            ->whereHas('user', function ($query) {
                $query->active();
            })
            ->get();

        $lateCount = 0;
        $absentCount = 0;
        $skippedLeave = 0;
        $skippedDuplicate = 0;

        foreach ($employees as $employee) {
            $hasLeave = LeaveRequest::query()
                ->approved()
                ->where('employee_id', $employee->id)
                ->whereDate('start_date', '<=', $date)
                ->whereDate('end_date', '>=', $date)
                ->exists();

            if ($hasLeave) {
                $skippedLeave++;

                continue;
            }

            $attendance = Attendance::query()
                ->where('employee_id', $employee->id)
                ->whereDate('attendance_date', $date)
                ->first();

            if ($attendance === null) {
                if ($absentPenalty <= 0) {
                    continue;
                }

                $payroll = $this->findOpenPayroll($employee->id);

                if ($payroll === null) {
                    continue;
                }

                $alreadyExists = $payroll->adjustments()
                    ->where('attendance_id', null)
                    ->deduction()
                    ->where('description', 'like', "%Bolos {$date->format('d/m/Y')}%")
                    ->exists();

                if ($alreadyExists) {
                    $skippedDuplicate++;

                    continue;
                }

                $payroll->adjustments()->create([
                    'type' => PayrollAdjustmentType::DEDUCTION,
                    'amount' => $absentPenalty,
                    'description' => "Bolos {$date->format('d/m/Y')}",
                    'created_by_id' => 1,
                ]);

                $payroll->load('adjustments');
                $payroll->recalculateAmounts();
                $payroll->save();

                $absentCount++;

                continue;
            }

            if ($latePenalty <= 0) {
                continue;
            }

            $checkInTime = Carbon::parse($attendance->check_in_at);

            if ($checkInTime->lte($scheduledCheckIn)) {
                continue;
            }

            $payroll = $this->findOpenPayroll($employee->id);

            if ($payroll === null) {
                continue;
            }

            $alreadyExists = $payroll->adjustments()
                ->where('attendance_id', $attendance->id)
                ->where('type', PayrollAdjustmentType::DEDUCTION)
                ->exists();

            if ($alreadyExists) {
                $skippedDuplicate++;

                continue;
            }

            $minutesLate = (int) $scheduledCheckIn->diffInMinutes($checkInTime);

            $payroll->adjustments()->create([
                'type' => PayrollAdjustmentType::DEDUCTION,
                'amount' => $latePenalty,
                'description' => "Terlambat {$date->format('d/m/Y')} ({$minutesLate} menit)",
                'attendance_id' => $attendance->id,
                'created_by_id' => 1,
            ]);

            $payroll->load('adjustments');
            $payroll->recalculateAmounts();
            $payroll->save();

            $lateCount++;
        }

        $this->info("Selesai memproses tanggal {$date->format('d/m/Y')}:");
        $this->info("  - Terlambat: {$lateCount} pegawai");
        $this->info("  - Bolos: {$absentCount} pegawai");
        $this->info("  - Lewati (cuti): {$skippedLeave} pegawai");
        $this->info("  - Lewati (duplikat): {$skippedDuplicate} pegawai");

        return self::SUCCESS;
    }

    private function findOpenPayroll(int $employeeId): ?Payroll
    {
        $period = PayrollPeriod::query()
            ->open()
            ->first();

        if ($period === null) {
            return null;
        }

        return Payroll::query()
            ->where('payroll_period_id', $period->id)
            ->where('employee_id', $employeeId)
            ->first();
    }
}
