<?php

namespace App\Services\HR;

use App\Models\HR\Attendance;
use App\Models\HR\Leave;
use App\Models\HR\LeaveBalance;
use App\Models\Tenants\Department;
use Carbon\CarbonPeriod;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class LeaveService
{
    /**
     * 휴가 목록 조회 (필터 + 페이지네이션)
     */
    public function getLeaves(array $filters = [], int $perPage = 20): LengthAwarePaginator
    {
        $tenantId = session('selected_tenant_id');

        $query = Leave::query()
            ->with([
                'user',
                'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId),
                'user.tenantProfiles.department',
                'approver',
            ])
            ->forTenant($tenantId);

        if (! empty($filters['q'])) {
            $search = $filters['q'];
            $query->whereHas('user', fn ($q) => $q->where('name', 'like', "%{$search}%"));
        }

        if (! empty($filters['user_id'])) {
            $query->where('user_id', $filters['user_id']);
        }

        if (! empty($filters['department_id'])) {
            $deptId = $filters['department_id'];
            $query->whereHas('user.tenantProfiles', function ($q) use ($tenantId, $deptId) {
                $q->where('tenant_id', $tenantId)->where('department_id', $deptId);
            });
        }

        if (! empty($filters['leave_type'])) {
            $query->where('leave_type', $filters['leave_type']);
        }

        if (! empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (! empty($filters['date_from']) && ! empty($filters['date_to'])) {
            $query->betweenDates($filters['date_from'], $filters['date_to']);
        } elseif (! empty($filters['date_from'])) {
            $query->where('start_date', '>=', $filters['date_from']);
        } elseif (! empty($filters['date_to'])) {
            $query->where('end_date', '<=', $filters['date_to']);
        }

        return $query
            ->orderByRaw("FIELD(status, 'pending', 'approved', 'rejected', 'cancelled')")
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);
    }

    /**
     * 휴가 신청 등록
     */
    public function storeLeave(array $data): Leave
    {
        $tenantId = session('selected_tenant_id');
        $days = $this->calculateDays($data['leave_type'], $data['start_date'], $data['end_date']);

        // 연차 차감 대상이면 잔여일수 검증
        if (in_array($data['leave_type'], Leave::DEDUCTIBLE_TYPES)) {
            $balance = LeaveBalance::query()
                ->forTenant($tenantId)
                ->forUser($data['user_id'])
                ->forYear(now()->year)
                ->first();

            if (! $balance || ! $balance->canUse($days)) {
                throw new \RuntimeException('잔여 연차가 부족합니다.');
            }
        }

        return Leave::create([
            'tenant_id' => $tenantId,
            'user_id' => $data['user_id'],
            'leave_type' => $data['leave_type'],
            'start_date' => $data['start_date'],
            'end_date' => $data['end_date'],
            'days' => $days,
            'reason' => $data['reason'] ?? null,
            'status' => 'pending',
            'created_by' => auth()->id(),
            'updated_by' => auth()->id(),
        ]);
    }

    /**
     * 승인 → LeaveBalance 차감 → Attendance 자동 생성
     */
    public function approve(int $id): ?Leave
    {
        $tenantId = session('selected_tenant_id');

        $leave = Leave::query()
            ->forTenant($tenantId)
            ->withStatus('pending')
            ->find($id);

        if (! $leave) {
            return null;
        }

        return DB::transaction(function () use ($leave, $tenantId) {
            $leave->update([
                'status' => 'approved',
                'approved_by' => auth()->id(),
                'approved_at' => now(),
                'updated_by' => auth()->id(),
            ]);

            // 연차 차감 대상이면 LeaveBalance 차감
            if ($leave->is_deductible) {
                $balance = LeaveBalance::query()
                    ->forTenant($tenantId)
                    ->forUser($leave->user_id)
                    ->forYear($leave->start_date->year)
                    ->first();

                if ($balance) {
                    $balance->useLeave($leave->days);
                }
            }

            // 기간 내 영업일마다 Attendance(vacation) 자동 생성
            $this->createAttendanceRecords($leave, $tenantId);

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

    /**
     * 반려
     */
    public function reject(int $id, ?string $reason = null): ?Leave
    {
        $tenantId = session('selected_tenant_id');

        $leave = Leave::query()
            ->forTenant($tenantId)
            ->withStatus('pending')
            ->find($id);

        if (! $leave) {
            return null;
        }

        $leave->update([
            'status' => 'rejected',
            'approved_by' => auth()->id(),
            'approved_at' => now(),
            'reject_reason' => $reason,
            'updated_by' => auth()->id(),
        ]);

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

    /**
     * 취소 → LeaveBalance 복원 → Attendance 삭제
     */
    public function cancel(int $id): ?Leave
    {
        $tenantId = session('selected_tenant_id');

        $leave = Leave::query()
            ->forTenant($tenantId)
            ->withStatus('approved')
            ->find($id);

        if (! $leave) {
            return null;
        }

        return DB::transaction(function () use ($leave, $tenantId) {
            $leave->update([
                'status' => 'cancelled',
                'updated_by' => auth()->id(),
            ]);

            // 연차 차감 대상이면 LeaveBalance 복원
            if ($leave->is_deductible) {
                $balance = LeaveBalance::query()
                    ->forTenant($tenantId)
                    ->forUser($leave->user_id)
                    ->forYear($leave->start_date->year)
                    ->first();

                if ($balance) {
                    $balance->restoreLeave($leave->days);
                }
            }

            // 해당 기간 vacation Attendance soft delete
            $this->deleteAttendanceRecords($leave, $tenantId);

            return $leave->fresh(['user']);
        });
    }

    /**
     * 전체 사원 잔여연차 요약
     */
    public function getBalanceSummary(?int $year = null): Collection
    {
        $tenantId = session('selected_tenant_id');
        $year = $year ?? now()->year;

        return LeaveBalance::query()
            ->with([
                'user',
                'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId),
                'user.tenantProfiles.department',
            ])
            ->forTenant($tenantId)
            ->forYear($year)
            ->orderBy('user_id')
            ->get();
    }

    /**
     * 개별 사원 잔여연차
     */
    public function getUserBalance(int $userId, ?int $year = null): ?LeaveBalance
    {
        $tenantId = session('selected_tenant_id');
        $year = $year ?? now()->year;

        return LeaveBalance::query()
            ->forTenant($tenantId)
            ->forUser($userId)
            ->forYear($year)
            ->first();
    }

    /**
     * 유형별/사원별 사용 통계
     */
    public function getUsageStats(?int $year = null): array
    {
        $tenantId = session('selected_tenant_id');
        $year = $year ?? now()->year;

        // 유형별 집계
        $byType = Leave::query()
            ->forTenant($tenantId)
            ->forYear($year)
            ->withStatus('approved')
            ->select('leave_type', DB::raw('COUNT(*) as count'), DB::raw('SUM(days) as total_days'))
            ->groupBy('leave_type')
            ->get()
            ->keyBy('leave_type');

        // 사원별 유형 크로스 테이블
        $byUser = Leave::query()
            ->forTenant($tenantId)
            ->forYear($year)
            ->withStatus('approved')
            ->with([
                'user',
                'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId),
                'user.tenantProfiles.department',
            ])
            ->select('user_id', 'leave_type', DB::raw('SUM(days) as total_days'))
            ->groupBy('user_id', 'leave_type')
            ->get()
            ->groupBy('user_id');

        return [
            'by_type' => $byType,
            'by_user' => $byUser,
            'year' => $year,
        ];
    }

    /**
     * 일수 자동 계산 (반차 = 0.5, 연차/기타 = 영업일수)
     */
    public function calculateDays(string $type, string $start, string $end): float
    {
        if (in_array($type, ['half_am', 'half_pm'])) {
            return 0.5;
        }

        $period = CarbonPeriod::create($start, $end);
        $businessDays = 0;

        foreach ($period as $date) {
            if (! $date->isWeekend()) {
                $businessDays++;
            }
        }

        return (float) $businessDays;
    }

    /**
     * CSV 내보내기용 데이터
     */
    public function getExportData(array $filters = []): Collection
    {
        $tenantId = session('selected_tenant_id');

        $query = Leave::query()
            ->with([
                'user',
                'user.tenantProfiles' => fn ($q) => $q->where('tenant_id', $tenantId),
                'user.tenantProfiles.department',
                'approver',
            ])
            ->forTenant($tenantId);

        if (! empty($filters['status'])) {
            $query->withStatus($filters['status']);
        }

        if (! empty($filters['leave_type'])) {
            $query->where('leave_type', $filters['leave_type']);
        }

        if (! empty($filters['date_from']) && ! empty($filters['date_to'])) {
            $query->betweenDates($filters['date_from'], $filters['date_to']);
        }

        return $query->orderBy('start_date', 'desc')->get();
    }

    /**
     * 부서 목록
     */
    public function getDepartments(): \Illuminate\Database\Eloquent\Collection
    {
        $tenantId = session('selected_tenant_id');

        return Department::query()
            ->where('is_active', true)
            ->when($tenantId, fn ($q) => $q->where('tenant_id', $tenantId))
            ->orderBy('sort_order')
            ->orderBy('name')
            ->get(['id', 'name', 'code']);
    }

    /**
     * 활성 사원 목록 (드롭다운용)
     */
    public function getActiveEmployees(): \Illuminate\Database\Eloquent\Collection
    {
        $tenantId = session('selected_tenant_id');

        return \App\Models\HR\Employee::query()
            ->with('user:id,name')
            ->forTenant($tenantId)
            ->activeEmployees()
            ->orderBy('display_name')
            ->get(['id', 'user_id', 'display_name', 'department_id']);
    }

    // =========================================================================
    // Private
    // =========================================================================

    /**
     * 승인 시 기간 내 영업일마다 Attendance(vacation) 자동 생성
     */
    private function createAttendanceRecords(Leave $leave, int $tenantId): void
    {
        $period = CarbonPeriod::create($leave->start_date, $leave->end_date);

        foreach ($period as $date) {
            if ($date->isWeekend()) {
                continue;
            }

            Attendance::updateOrCreate(
                [
                    'tenant_id' => $tenantId,
                    'user_id' => $leave->user_id,
                    'base_date' => $date->toDateString(),
                ],
                [
                    'status' => 'vacation',
                    'remarks' => $leave->reason ? mb_substr($leave->reason, 0, 100) : null,
                    'updated_by' => auth()->id(),
                ]
            );
        }
    }

    /**
     * 취소 시 해당 기간 vacation Attendance soft delete
     */
    private function deleteAttendanceRecords(Leave $leave, int $tenantId): void
    {
        Attendance::query()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $leave->user_id)
            ->where('status', 'vacation')
            ->whereBetween('base_date', [
                $leave->start_date->toDateString(),
                $leave->end_date->toDateString(),
            ])
            ->update(['deleted_by' => auth()->id()]);

        Attendance::query()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $leave->user_id)
            ->where('status', 'vacation')
            ->whereBetween('base_date', [
                $leave->start_date->toDateString(),
                $leave->end_date->toDateString(),
            ])
            ->delete();
    }
}
