<?php

namespace App\Services;

use App\Models\Attendance;
use App\Models\AttendanceCorrection;
use App\Models\Company;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Support\Str;

class AttendanceCorrectionService
{
    /**
     * Create a new correction request
     */
    public function createRequest(array $data, User $user): AttendanceCorrection
    {
        $company = $user->company;
        $targetDate = Carbon::parse($data['target_date']);
        $isBackdateMode = $company->backdate_enabled ?? false;

        // Get existing attendance for this date
        $attendance = Attendance::where('user_id', $user->id)
            ->whereDate('date', $targetDate)
            ->first();

        // Validate quota if not in backdate mode
        if (!$isBackdateMode) {
            $this->validateQuota($user, $company);
        }

        // Prepare correction data
        $correctionData = [
            'id' => Str::uuid(),
            'user_id' => $user->id,
            'company_id' => $company->id,
            'attendance_id' => $attendance?->id,
            'target_date' => $targetDate,
            'correction_type' => $data['correction_type'] ?? AttendanceCorrection::TYPE_DURATION,
            'original_check_in' => $attendance?->check_in_time?->format('H:i'),
            'original_check_out' => $attendance?->check_out_time?->format('H:i'),
            'original_duration' => $attendance?->work_duration,
            'corrected_check_in' => $data['corrected_check_in'] ?? null,
            'corrected_check_out' => $data['corrected_check_out'] ?? null,
            'corrected_duration' => $data['corrected_duration'] ?? null,
            'reason' => $data['reason'] ?? null,
            'status' => AttendanceCorrection::STATUS_PENDING,
        ];

        // Handle backdate mode - auto approve
        if ($isBackdateMode) {
            $correctionData['status'] = AttendanceCorrection::STATUS_AUTO_APPROVED;
            $correctionData['is_auto_approved'] = true;
            $correctionData['approved_at'] = $this->generateNaturalApprovalTimestamp($targetDate);
            $correctionData['simulated_request_at'] = $data['simulated_request_at'] ?? $this->generateNaturalRequestTimestamp($targetDate);
        }

        $correction = AttendanceCorrection::create($correctionData);

        // If auto-approved, apply the correction
        if ($correction->is_auto_approved) {
            $this->applyCorrection($correction);
        }

        return $correction->fresh(['user', 'attendance', 'approver']);
    }

    /**
     * Approve a correction request
     */
    public function approve(AttendanceCorrection $correction, User $approver): AttendanceCorrection
    {
        if (!$correction->isPending()) {
            throw new \Exception('Koreksi ini sudah diproses sebelumnya');
        }

        $correction->update([
            'status' => AttendanceCorrection::STATUS_APPROVED,
            'approved_by' => $approver->id,
            'approved_at' => now(),
        ]);

        // Apply the correction to attendance
        $this->applyCorrection($correction);

        return $correction->fresh(['user', 'attendance', 'approver']);
    }

    /**
     * Reject a correction request
     */
    public function reject(AttendanceCorrection $correction, User $approver, ?string $reason = null): AttendanceCorrection
    {
        if (!$correction->isPending()) {
            throw new \Exception('Koreksi ini sudah diproses sebelumnya');
        }

        $correction->update([
            'status' => AttendanceCorrection::STATUS_REJECTED,
            'approved_by' => $approver->id,
            'approved_at' => now(),
            'rejection_reason' => $reason,
        ]);

        return $correction->fresh(['user', 'attendance', 'approver']);
    }

    /**
     * Apply correction to attendance record
     */
    protected function applyCorrection(AttendanceCorrection $correction): void
    {
        $attendance = $correction->attendance;
        if (!$attendance) {
            return;
        }

        $updateData = [];

        switch ($correction->correction_type) {
            case AttendanceCorrection::TYPE_CHECK_IN:
                if ($correction->corrected_check_in) {
                    $updateData['check_in_time'] = Carbon::parse($correction->target_date)
                        ->setTimeFromTimeString($correction->corrected_check_in);
                }
                break;

            case AttendanceCorrection::TYPE_CHECK_OUT:
                if ($correction->corrected_check_out) {
                    $updateData['check_out_time'] = Carbon::parse($correction->target_date)
                        ->setTimeFromTimeString($correction->corrected_check_out);
                }
                break;

            case AttendanceCorrection::TYPE_BOTH:
                if ($correction->corrected_check_in) {
                    $updateData['check_in_time'] = Carbon::parse($correction->target_date)
                        ->setTimeFromTimeString($correction->corrected_check_in);
                }
                if ($correction->corrected_check_out) {
                    $updateData['check_out_time'] = Carbon::parse($correction->target_date)
                        ->setTimeFromTimeString($correction->corrected_check_out);
                }
                break;

            case AttendanceCorrection::TYPE_DURATION:
                if ($correction->corrected_duration !== null) {
                    $updateData['work_duration'] = $correction->corrected_duration;
                }
                break;
        }

        if (!empty($updateData)) {
            // Recalculate work duration if check times changed
            if (isset($updateData['check_in_time']) || isset($updateData['check_out_time'])) {
                $checkIn = $updateData['check_in_time'] ?? $attendance->check_in_time;
                $checkOut = $updateData['check_out_time'] ?? $attendance->check_out_time;

                if ($checkIn && $checkOut) {
                    $checkInCarbon = $checkIn instanceof Carbon ? $checkIn : Carbon::parse($checkIn);
                    $checkOutCarbon = $checkOut instanceof Carbon ? $checkOut : Carbon::parse($checkOut);
                    $updateData['work_duration'] = $checkOutCarbon->diffInMinutes($checkInCarbon);
                }
            }

            $attendance->update($updateData);
        }
    }

    /**
     * Validate quota for correction requests
     */
    protected function validateQuota(User $user, Company $company): void
    {
        $quotaMax = $company->correction_quota_max;
        if ($quotaMax === null) {
            return; // No limit
        }

        $quotaPeriod = $company->correction_quota_period ?? 'monthly';
        $usedCount = $this->getQuotaUsed($user, $quotaPeriod);

        if ($usedCount >= $quotaMax) {
            throw new \Exception("Kuota koreksi absen Anda sudah habis untuk periode ini ({$usedCount}/{$quotaMax})");
        }
    }

    /**
     * Get quota usage for user
     */
    public function getQuotaUsed(User $user, string $period): int
    {
        $query = AttendanceCorrection::forUser($user->id)
            ->whereIn('status', [
                AttendanceCorrection::STATUS_PENDING,
                AttendanceCorrection::STATUS_APPROVED,
                AttendanceCorrection::STATUS_AUTO_APPROVED,
            ]);

        switch ($period) {
            case 'daily':
                $query->whereDate('created_at', today());
                break;
            case 'weekly':
                $query->whereBetween('created_at', [now()->startOfWeek(), now()->endOfWeek()]);
                break;
            case 'monthly':
                $query->whereMonth('created_at', now()->month)
                      ->whereYear('created_at', now()->year);
                break;
            case 'yearly':
                $query->whereYear('created_at', now()->year);
                break;
        }

        return $query->count();
    }

    /**
     * Get quota info for user
     */
    public function getQuotaInfo(User $user): array
    {
        $company = $user->company;
        $quotaMax = $company->correction_quota_max;
        $quotaPeriod = $company->correction_quota_period ?? 'monthly';

        if ($quotaMax === null) {
            return [
                'quota_max' => null,
                'quota_used' => 0,
                'quota_remaining' => null,
                'quota_period' => $quotaPeriod,
                'has_limit' => false,
            ];
        }

        $quotaUsed = $this->getQuotaUsed($user, $quotaPeriod);

        return [
            'quota_max' => $quotaMax,
            'quota_used' => $quotaUsed,
            'quota_remaining' => max(0, $quotaMax - $quotaUsed),
            'quota_period' => $quotaPeriod,
            'has_limit' => true,
        ];
    }

    /**
     * Generate natural request timestamp for backdate mode
     */
    protected function generateNaturalRequestTimestamp(Carbon $targetDate): Carbon
    {
        // Request time: between 08:00 - 17:00 on the target date
        $hour = rand(8, 16);
        $minute = rand(0, 59);

        return $targetDate->copy()->setTime($hour, $minute, rand(0, 59));
    }

    /**
     * Generate natural approval timestamp for backdate mode
     */
    protected function generateNaturalApprovalTimestamp(Carbon $targetDate): Carbon
    {
        // Approval: 1-7 working days after request
        $daysLater = rand(1, 7);
        $approvalDate = $targetDate->copy()->addDays($daysLater);

        // Skip weekends
        while ($approvalDate->isWeekend()) {
            $approvalDate->addDay();
        }

        // Approval time: between 08:00 - 17:00
        $hour = rand(8, 16);
        $minute = rand(0, 59);

        return $approvalDate->setTime($hour, $minute, rand(0, 59));
    }
}
