<?php

namespace App\Services;

use App\Models\Tenants\Leave;
use App\Models\Tenants\LeaveBalance;
use App\Models\Tenants\LeaveGrant;
use App\Models\Tenants\TenantUserProfile;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class LeaveService extends Service
{
    /**
     * 휴가 목록 조회
     */
    public function index(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();

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

        // 사용자 필터
        if (! empty($params['user_id'])) {
            $query->where('user_id', $params['user_id']);
        }

        // 상태 필터
        if (! empty($params['status'])) {
            $query->where('status', $params['status']);
        }

        // 휴가 유형 필터
        if (! empty($params['leave_type'])) {
            $query->where('leave_type', $params['leave_type']);
        }

        // 날짜 범위 필터
        if (! empty($params['date_from'])) {
            $query->where('start_date', '>=', $params['date_from']);
        }
        if (! empty($params['date_to'])) {
            $query->where('end_date', '<=', $params['date_to']);
        }

        // 연도 필터
        if (! empty($params['year'])) {
            $query->whereYear('start_date', $params['year']);
        }

        // 부서 필터
        if (! empty($params['department_id'])) {
            $query->whereHas('user.tenantProfile', function ($q) use ($params) {
                $q->where('department_id', $params['department_id']);
            });
        }

        // 정렬
        $sortBy = $params['sort_by'] ?? 'created_at';
        $sortDir = $params['sort_dir'] ?? 'desc';
        $query->orderBy($sortBy, $sortDir);

        // 페이지네이션
        $perPage = $params['per_page'] ?? 20;

        return $query->paginate($perPage);
    }

    /**
     * 휴가 상세 조회
     */
    public function show(int $id): Leave
    {
        $tenantId = $this->tenantId();

        return Leave::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'user:id,name,email',
                'userProfile' => fn ($q) => $q->where('tenant_id', $tenantId),
                'userProfile.department:id,name',
                'approver:id,name',
            ])
            ->findOrFail($id);
    }

    /**
     * 휴가 신청
     */
    public function store(array $data): Leave
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            // 신청자 ID (관리자가 대리 신청 가능)
            $applicantId = $data['user_id'] ?? $userId;

            // 잔여 휴가 확인 (연차/반차만)
            if (in_array($data['leave_type'], [Leave::TYPE_ANNUAL, Leave::TYPE_HALF_AM, Leave::TYPE_HALF_PM])) {
                $year = \Carbon\Carbon::parse($data['start_date'])->year;
                $balance = $this->getOrCreateBalance($tenantId, $applicantId, $year);

                if (! $balance->canUse($data['days'])) {
                    throw new BadRequestHttpException(__('error.leave.insufficient_balance'));
                }
            }

            // 중복 휴가 확인
            $overlapping = Leave::query()
                ->where('tenant_id', $tenantId)
                ->where('user_id', $applicantId)
                ->whereIn('status', [Leave::STATUS_PENDING, Leave::STATUS_APPROVED])
                ->where(function ($q) use ($data) {
                    $q->whereBetween('start_date', [$data['start_date'], $data['end_date']])
                        ->orWhereBetween('end_date', [$data['start_date'], $data['end_date']])
                        ->orWhere(function ($q2) use ($data) {
                            $q2->where('start_date', '<=', $data['start_date'])
                                ->where('end_date', '>=', $data['end_date']);
                        });
                })
                ->exists();

            if ($overlapping) {
                throw new BadRequestHttpException(__('error.leave.overlapping'));
            }

            $leave = Leave::create([
                'tenant_id' => $tenantId,
                'user_id' => $applicantId,
                'leave_type' => $data['leave_type'],
                'start_date' => $data['start_date'],
                'end_date' => $data['end_date'],
                'days' => $data['days'],
                'reason' => $data['reason'] ?? null,
                'status' => Leave::STATUS_PENDING,
                'created_by' => $userId,
                'updated_by' => $userId,
            ]);

            return $leave->fresh(['user:id,name,email']);
        });
    }

    /**
     * 휴가 수정 (pending 상태만)
     */
    public function update(int $id, array $data): Leave
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $data, $tenantId, $userId) {
            $leave = Leave::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $leave->isEditable()) {
                throw new BadRequestHttpException(__('error.leave.not_editable'));
            }

            // 휴가 기간이 변경된 경우 잔여 휴가 확인
            if (isset($data['days']) && in_array($leave->leave_type, [Leave::TYPE_ANNUAL, Leave::TYPE_HALF_AM, Leave::TYPE_HALF_PM])) {
                $year = \Carbon\Carbon::parse($data['start_date'] ?? $leave->start_date)->year;
                $balance = $this->getOrCreateBalance($tenantId, $leave->user_id, $year);

                if (! $balance->canUse($data['days'])) {
                    throw new BadRequestHttpException(__('error.leave.insufficient_balance'));
                }
            }

            $leave->fill([
                'leave_type' => $data['leave_type'] ?? $leave->leave_type,
                'start_date' => $data['start_date'] ?? $leave->start_date,
                'end_date' => $data['end_date'] ?? $leave->end_date,
                'days' => $data['days'] ?? $leave->days,
                'reason' => $data['reason'] ?? $leave->reason,
                'updated_by' => $userId,
            ]);

            $leave->save();

            return $leave->fresh(['user:id,name,email']);
        });
    }

    /**
     * 휴가 취소/삭제 (pending 상태만)
     */
    public function destroy(int $id): bool
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $leave = Leave::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $leave->isEditable()) {
                throw new BadRequestHttpException(__('error.leave.not_editable'));
            }

            $leave->deleted_by = $userId;
            $leave->save();
            $leave->delete();

            return true;
        });
    }

    /**
     * 휴가 승인
     */
    public function approve(int $id, ?string $comment = null): Leave
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $leave = Leave::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $leave->isApprovable()) {
                throw new BadRequestHttpException(__('error.leave.not_approvable'));
            }

            // 잔여 휴가 차감 (연차/반차만)
            if (in_array($leave->leave_type, [Leave::TYPE_ANNUAL, Leave::TYPE_HALF_AM, Leave::TYPE_HALF_PM])) {
                $year = \Carbon\Carbon::parse($leave->start_date)->year;
                $balance = $this->getOrCreateBalance($tenantId, $leave->user_id, $year);

                if (! $balance->canUse($leave->days)) {
                    throw new BadRequestHttpException(__('error.leave.insufficient_balance'));
                }

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

            $leave->status = Leave::STATUS_APPROVED;
            $leave->approved_by = $userId;
            $leave->approved_at = now();
            $leave->updated_by = $userId;
            $leave->save();

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

    /**
     * 휴가 반려
     */
    public function reject(int $id, string $reason): Leave
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $reason, $tenantId, $userId) {
            $leave = Leave::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $leave->isApprovable()) {
                throw new BadRequestHttpException(__('error.leave.not_approvable'));
            }

            $leave->status = Leave::STATUS_REJECTED;
            $leave->approved_by = $userId;
            $leave->approved_at = now();
            $leave->reject_reason = $reason;
            $leave->updated_by = $userId;
            $leave->save();

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

    /**
     * 승인된 휴가 취소 (휴가 복원)
     */
    public function cancel(int $id, ?string $reason = null): Leave
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $reason, $tenantId, $userId) {
            $leave = Leave::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            if (! $leave->isCancellable()) {
                throw new BadRequestHttpException(__('error.leave.not_cancellable'));
            }

            // 이미 승인된 휴가라면 잔여일수 복원
            if ($leave->status === Leave::STATUS_APPROVED) {
                if (in_array($leave->leave_type, [Leave::TYPE_ANNUAL, Leave::TYPE_HALF_AM, Leave::TYPE_HALF_PM])) {
                    $year = \Carbon\Carbon::parse($leave->start_date)->year;
                    $balance = $this->getOrCreateBalance($tenantId, $leave->user_id, $year);
                    $balance->restoreLeave($leave->days);
                }
            }

            $leave->status = Leave::STATUS_CANCELLED;
            $leave->reject_reason = $reason;
            $leave->updated_by = $userId;
            $leave->save();

            return $leave->fresh(['user:id,name,email']);
        });
    }

    /**
     * 전체 직원 휴가 사용현황 목록 조회
     * TenantUserProfile 기준으로 전체 직원 조회 후 LeaveBalance LEFT JOIN
     */
    public function getAllBalances(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();
        $year = $params['year'] ?? now()->year;

        $query = TenantUserProfile::query()
            ->where('tenant_id', $tenantId)
            ->whereIn('employee_status', ['active', 'leave']) // 재직 + 휴직 직원 포함
            ->with([
                'user:id,name,email',
                'department:id,name',
            ])
            ->addSelect([
                'tenant_user_profiles.*',
                'leave_balance_total' => LeaveBalance::selectRaw('total_days')
                    ->whereColumn('leave_balances.user_id', 'tenant_user_profiles.user_id')
                    ->where('leave_balances.tenant_id', $tenantId)
                    ->where('leave_balances.year', $year)
                    ->limit(1),
                'leave_balance_used' => LeaveBalance::selectRaw('used_days')
                    ->whereColumn('leave_balances.user_id', 'tenant_user_profiles.user_id')
                    ->where('leave_balances.tenant_id', $tenantId)
                    ->where('leave_balances.year', $year)
                    ->limit(1),
            ]);

        // 부서 필터
        if (! empty($params['department_id'])) {
            $query->where('department_id', $params['department_id']);
        }

        // 검색 (사용자명)
        if (! empty($params['search'])) {
            $query->whereHas('user', function ($q) use ($params) {
                $q->where('name', 'like', '%'.$params['search'].'%');
            });
        }

        // 정렬
        $sortBy = $params['sort_by'] ?? 'user_id';
        $sortDir = $params['sort_dir'] ?? 'asc';

        if ($sortBy === 'user_id') {
            $query->orderBy('user_id', $sortDir);
        } elseif ($sortBy === 'department') {
            $query->orderBy('department_id', $sortDir);
        } else {
            $query->orderBy($sortBy, $sortDir);
        }

        // 페이지네이션
        $perPage = $params['per_page'] ?? 20;

        return $query->paginate($perPage);
    }

    /**
     * 내 잔여 휴가 조회
     */
    public function getMyBalance(?int $year = null): LeaveBalance
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();
        $year = $year ?? now()->year;

        return $this->getOrCreateBalance($tenantId, $userId, $year);
    }

    /**
     * 특정 사용자 잔여 휴가 조회
     */
    public function getUserBalance(int $userId, ?int $year = null): LeaveBalance
    {
        $tenantId = $this->tenantId();
        $year = $year ?? now()->year;

        return $this->getOrCreateBalance($tenantId, $userId, $year);
    }

    /**
     * 잔여 휴가 설정
     */
    public function setBalance(int $userId, int $year, float $totalDays): LeaveBalance
    {
        $tenantId = $this->tenantId();

        $balance = LeaveBalance::query()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $userId)
            ->where('year', $year)
            ->first();

        if ($balance) {
            $balance->total_days = $totalDays;
            $balance->save();
        } else {
            $balance = LeaveBalance::create([
                'tenant_id' => $tenantId,
                'user_id' => $userId,
                'year' => $year,
                'total_days' => $totalDays,
                'used_days' => 0,
            ]);
        }

        return $balance->fresh();
    }

    /**
     * 잔여 휴가 조회 또는 생성
     */
    private function getOrCreateBalance(int $tenantId, int $userId, int $year): LeaveBalance
    {
        $balance = LeaveBalance::query()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $userId)
            ->where('year', $year)
            ->first();

        if (! $balance) {
            $balance = LeaveBalance::create([
                'tenant_id' => $tenantId,
                'user_id' => $userId,
                'year' => $year,
                'total_days' => 15, // 기본 연차 15일
                'used_days' => 0,
            ]);
        }

        return $balance;
    }

    // =========================================================================
    // 휴가 부여 관련 메서드
    // =========================================================================

    /**
     * 휴가 부여 이력 목록 조회
     */
    public function getGrants(array $params): LengthAwarePaginator
    {
        $tenantId = $this->tenantId();

        $query = LeaveGrant::query()
            ->where('tenant_id', $tenantId)
            ->with([
                'user:id,name,email',
                'user.tenantProfile' => function ($q) use ($tenantId) {
                    $q->where('tenant_id', $tenantId)
                        ->with('department:id,name');
                },
                'creator:id,name',
            ]);

        // 사용자 필터
        if (! empty($params['user_id'])) {
            $query->where('user_id', $params['user_id']);
        }

        // 부여 유형 필터
        if (! empty($params['grant_type'])) {
            $query->where('grant_type', $params['grant_type']);
        }

        // 날짜 범위 필터
        if (! empty($params['date_from'])) {
            $query->where('grant_date', '>=', $params['date_from']);
        }
        if (! empty($params['date_to'])) {
            $query->where('grant_date', '<=', $params['date_to']);
        }

        // 연도 필터
        if (! empty($params['year'])) {
            $query->whereYear('grant_date', $params['year']);
        }

        // 부서 필터
        if (! empty($params['department_id'])) {
            $query->whereHas('user.tenantProfile', function ($q) use ($params, $tenantId) {
                $q->where('tenant_id', $tenantId)
                    ->where('department_id', $params['department_id']);
            });
        }

        // 검색 (사용자명)
        if (! empty($params['search'])) {
            $query->whereHas('user', function ($q) use ($params) {
                $q->where('name', 'like', '%'.$params['search'].'%');
            });
        }

        // 정렬
        $sortBy = $params['sort_by'] ?? 'grant_date';
        $sortDir = $params['sort_dir'] ?? 'desc';
        $query->orderBy($sortBy, $sortDir);

        // 페이지네이션
        $perPage = $params['per_page'] ?? 20;

        return $query->paginate($perPage);
    }

    /**
     * 휴가 부여
     */
    public function storeGrant(array $data): LeaveGrant
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($data, $tenantId, $userId) {
            $grant = LeaveGrant::create([
                'tenant_id' => $tenantId,
                'user_id' => $data['user_id'],
                'grant_type' => $data['grant_type'],
                'grant_date' => $data['grant_date'],
                'grant_days' => $data['grant_days'],
                'reason' => $data['reason'] ?? null,
                'created_by' => $userId,
            ]);

            // 연차/월차인 경우 LeaveBalance의 total_days에 추가
            if (in_array($data['grant_type'], [LeaveGrant::TYPE_ANNUAL, LeaveGrant::TYPE_MONTHLY])) {
                $year = \Carbon\Carbon::parse($data['grant_date'])->year;
                $balance = $this->getOrCreateBalance($tenantId, $data['user_id'], $year);
                $balance->total_days += $data['grant_days'];
                $balance->save();
            }

            return $grant->fresh(['user:id,name,email', 'creator:id,name']);
        });
    }

    /**
     * 휴가 부여 삭제 (soft delete)
     */
    public function destroyGrant(int $id): bool
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        return DB::transaction(function () use ($id, $tenantId, $userId) {
            $grant = LeaveGrant::query()
                ->where('tenant_id', $tenantId)
                ->findOrFail($id);

            // 연차/월차인 경우 LeaveBalance의 total_days에서 차감
            if (in_array($grant->grant_type, [LeaveGrant::TYPE_ANNUAL, LeaveGrant::TYPE_MONTHLY])) {
                $year = \Carbon\Carbon::parse($grant->grant_date)->year;
                $balance = LeaveBalance::query()
                    ->where('tenant_id', $tenantId)
                    ->where('user_id', $grant->user_id)
                    ->where('year', $year)
                    ->first();

                if ($balance) {
                    $balance->total_days = max(0, $balance->total_days - $grant->grant_days);
                    $balance->save();
                }
            }

            $grant->deleted_by = $userId;
            $grant->save();
            $grant->delete();

            return true;
        });
    }
}
