<?php

declare(strict_types=1);

namespace Kylin\Hr\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\Eloquent\Model;
use Kylin\Hr\Enums\ApprovalStatus;
use Kylin\Hr\Models\LeaveRequest;
use Kylin\Hr\Models\OvertimeRequest;
use Kylin\Hr\Models\AttendanceRecord;
use Kylin\Hr\Enums\AttendanceStatus;
use Throwable;

/**
 * 考勤服务
 *
 * 处理考勤打卡、请假/加班审批、考勤统计等业务逻辑。
 */
class AttendanceService
{
    /**
     * 打卡签到
     */
    public function clockIn(int $employeeId, array $data = []): AttendanceRecord
    {
        $today = now()->toDateString();

        return DB::transaction(function () use ($employeeId, $today, $data): AttendanceRecord {
            $record = AttendanceRecord::firstOrNew([
                'employee_id' => $employeeId,
                'date' => $today,
            ]);

            if ($record->exists && $record->check_in_time !== null) {
                // 签退
                $record->check_out_time = now();
                $record->work_hours = $record->check_in_time->diffInMinutes(now()) / 60;
                $record->save();

                $this->audit('clock_out', $record);
            } else {
                // 签到
                $record->check_in_time = now();
                $record->status = AttendanceStatus::Normal;
                $record->save();

                $this->audit('clock_in', $record);
            }

            return $record;
        });
    }

    /**
     * 提交请假申请
     */
    public function submitLeaveRequest(array $data): LeaveRequest
    {
        return DB::transaction(function () use ($data): LeaveRequest {
            $leave = LeaveRequest::create($data);

            Log::info('请假申请已提交', [
                'leave_id' => $leave->id,
                'employee_id' => $leave->employee_id,
            ]);

            $this->audit('leave_submitted', $leave, ['employee_id' => $leave->employee_id]);

            return $leave;
        });
    }

    /**
     * 审批请假
     */
    public function approveLeave(int $leaveId, int $approverId, bool $approved, ?string $opinion = null): LeaveRequest
    {
        $leave = LeaveRequest::findOrFail($leaveId);

        $leave->update([
            'approval_status' => $approved ? ApprovalStatus::Approved : ApprovalStatus::Rejected,
            'approver_id' => $approverId,
            'approval_opinion' => $opinion,
            'approved_at' => now(),
        ]);

        $this->audit('leave_approved', $leave, [
            'approved' => $approved,
            'approver_id' => $approverId,
        ]);

        return $leave->fresh();
    }

    /**
     * 提交加班申请
     */
    public function submitOvertimeRequest(array $data): OvertimeRequest
    {
        return DB::transaction(function () use ($data): OvertimeRequest {
            $overtime = OvertimeRequest::create($data);

            Log::info('加班申请已提交', [
                'overtime_id' => $overtime->id,
                'employee_id' => $overtime->employee_id,
            ]);

            $this->audit('overtime_submitted', $overtime, ['employee_id' => $overtime->employee_id]);

            return $overtime;
        });
    }

    /**
     * 获取考勤记录
     */
    public function getAttendanceRecords(int $employeeId, string $startDate, string $endDate)
    {
        return AttendanceRecord::where('employee_id', $employeeId)
            ->dateRange($startDate, $endDate)
            ->orderBy('date', 'desc')
            ->get();
    }

    /**
     * 记录考勤审计日志
     *
     * 依据《劳动法》和《劳动合同法》，
     * 考勤打卡、请假审批等操作需记录审计日志，便于劳动争议处理。
     */
    private function audit(string $event, Model $model, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => $model::class,
                'model_id' => $model->id,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'hr-attendance',
            ]);
        } catch (\Throwable $e) {
            logger()->warning('考勤审计日志写入失败', [
                'error' => $e->getMessage(),
                'event' => $event,
            ]);
        }
    }
}
