<?php

namespace App\Services\HR;

use App\Models\HR\Employee;
use App\Models\HR\Position;
use App\Models\Tenants\Department;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;

class EmployeeService
{
    /**
     * 사원 목록 조회 (페이지네이션)
     */
    public function getEmployees(array $filters = [], int $perPage = 20): LengthAwarePaginator
    {
        $tenantId = session('selected_tenant_id');

        $query = Employee::query()
            ->with(['user', 'department'])
            ->forTenant($tenantId);

        // 검색 필터 (이름, 이메일, 연락처)
        if (! empty($filters['q'])) {
            $search = $filters['q'];
            $query->where(function ($q) use ($search) {
                $q->where('display_name', 'like', "%{$search}%")
                    ->orWhereHas('user', function ($uq) use ($search) {
                        $uq->where('name', 'like', "%{$search}%")
                            ->orWhere('email', 'like', "%{$search}%")
                            ->orWhere('phone', 'like', "%{$search}%");
                    });
            });
        }

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

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

        // 정렬
        $sortBy = $filters['sort_by'] ?? 'hire_date_asc';
        switch ($sortBy) {
            case 'hire_date_asc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) ASC");
                break;
            case 'hire_date_desc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) DESC");
                break;
            case 'resign_date_asc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.resign_date')) ASC");
                break;
            case 'resign_date_desc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.resign_date')) DESC");
                break;
            default:
                $query->orderByRaw("FIELD(employee_status, 'active', 'leave', 'resigned')")
                    ->orderBy('created_at', 'desc');
                break;
        }

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

    /**
     * 사원 상세 조회
     */
    public function getEmployeeById(int $id): ?Employee
    {
        $tenantId = session('selected_tenant_id');

        return Employee::query()
            ->with(['user', 'department', 'manager'])
            ->forTenant($tenantId)
            ->find($id);
    }

    /**
     * 사원 통계
     */
    public function getStats(): array
    {
        $tenantId = session('selected_tenant_id');

        $baseQuery = Employee::query()->forTenant($tenantId);

        return [
            'total' => (clone $baseQuery)->count(),
            'active' => (clone $baseQuery)->where('employee_status', 'active')->count(),
            'leave' => (clone $baseQuery)->where('employee_status', 'leave')->count(),
            'resigned' => (clone $baseQuery)->where('employee_status', 'resigned')->count(),
        ];
    }

    /**
     * 테넌트 소속이지만 사원 미등록인 사용자 검색
     */
    public function searchTenantUsers(string $query): array
    {
        $tenantId = session('selected_tenant_id');

        $builder = User::query()
            ->select('users.id', 'users.name', 'users.email', 'users.phone')
            ->join('user_tenants as ut', function ($join) use ($tenantId) {
                $join->on('users.id', '=', 'ut.user_id')
                    ->where('ut.tenant_id', $tenantId)
                    ->whereNull('ut.deleted_at');
            })
            ->leftJoin('tenant_user_profiles as tup', function ($join) use ($tenantId) {
                $join->on('users.id', '=', 'tup.user_id')
                    ->where('tup.tenant_id', $tenantId);
            })
            ->whereNull('tup.id')
            ->whereNull('users.deleted_at');

        if ($query !== '') {
            $like = "%{$query}%";
            $builder->where(function ($q) use ($like) {
                $q->where('users.name', 'like', $like)
                    ->orWhere('users.email', 'like', $like)
                    ->orWhere('users.phone', 'like', $like);
            });
        }

        return $builder->orderBy('users.name')->limit(20)->get()->toArray();
    }

    /**
     * 사원 등록 (User + TenantUserProfile 동시 생성)
     */
    public function createEmployee(array $data): Employee
    {
        $tenantId = session('selected_tenant_id');

        return DB::transaction(function () use ($data, $tenantId) {
            // 기존 사용자 선택 분기
            if (! empty($data['existing_user_id'])) {
                $user = User::findOrFail($data['existing_user_id']);

                // 테넌트 소속 검증
                $isMember = $user->tenants()
                    ->wherePivot('tenant_id', $tenantId)
                    ->wherePivotNull('deleted_at')
                    ->exists();

                if (! $isMember) {
                    throw new \RuntimeException('해당 사용자는 현재 테넌트에 소속되어 있지 않습니다.');
                }

                // 이미 사원 등록 여부 확인
                $alreadyEmployee = Employee::where('tenant_id', $tenantId)
                    ->where('user_id', $user->id)
                    ->exists();

                if ($alreadyEmployee) {
                    throw new \RuntimeException('이미 사원으로 등록된 사용자입니다.');
                }
            } else {
                // 신규 사용자 생성
                $loginId = ! empty($data['email'])
                    ? Str::before($data['email'], '@')
                    : 'EMP_'.strtolower(Str::random(6));

                while (User::where('user_id', $loginId)->exists()) {
                    $loginId = $loginId.'_'.Str::random(3);
                }

                $email = ! empty($data['email'])
                    ? $data['email']
                    : $loginId.'@placeholder.local';

                while (User::where('email', $email)->exists()) {
                    $email = $loginId.'_'.Str::random(3).'@placeholder.local';
                }

                $user = User::create([
                    'user_id' => $loginId,
                    'name' => $data['name'],
                    'email' => $email,
                    'phone' => $data['phone'] ?? null,
                    'password' => Hash::make('sam1234!'),
                    'role' => 'ops',
                    'is_active' => true,
                    'must_change_password' => true,
                    'created_by' => auth()->id(),
                ]);

                if ($tenantId) {
                    $user->tenants()->attach($tenantId, [
                        'is_active' => true,
                        'is_default' => true,
                        'joined_at' => now(),
                    ]);
                }
            }

            // json_extra 구성
            $jsonExtra = [];
            foreach (['hire_date', 'resign_date', 'address', 'emergency_contact', 'resident_number'] as $key) {
                if (! empty($data[$key])) {
                    $jsonExtra[$key] = $data[$key];
                }
            }

            // 급여이체정보 (bank_account 객체)
            if (! empty($data['bank_account']) && is_array($data['bank_account'])) {
                $bankAccount = array_filter($data['bank_account'], fn ($v) => $v !== null && $v !== '');
                if (! empty($bankAccount)) {
                    $jsonExtra['bank_account'] = $bankAccount;
                }
            }

            // 부양가족 정보 (dependents 배열)
            if (! empty($data['dependents']) && is_array($data['dependents'])) {
                $dependents = array_values(array_filter($data['dependents'], function ($dep) {
                    return ! empty($dep['name']);
                }));
                if (! empty($dependents)) {
                    $jsonExtra['dependents'] = $dependents;
                }
            }

            // Employee(TenantUserProfile) 생성
            $employee = Employee::create([
                'tenant_id' => $tenantId,
                'user_id' => $user->id,
                'department_id' => $data['department_id'] ?? null,
                'position_key' => $data['position_key'] ?? null,
                'job_title_key' => $data['job_title_key'] ?? null,
                'work_location_key' => $data['work_location_key'] ?? null,
                'employment_type_key' => $data['employment_type_key'] ?? null,
                'employee_status' => $data['employee_status'] ?? 'active',
                'manager_user_id' => $data['manager_user_id'] ?? null,
                'display_name' => $data['display_name'] ?? $data['name'],
                'json_extra' => ! empty($jsonExtra) ? $jsonExtra : null,
            ]);

            return $employee->load(['user', 'department']);
        });
    }

    /**
     * 사원 정보 수정
     */
    public function updateEmployee(int $id, array $data): ?Employee
    {
        $employee = $this->getEmployeeById($id);
        if (! $employee) {
            return null;
        }

        // 기본 필드 업데이트
        $updateData = array_filter([
            'department_id' => $data['department_id'] ?? null,
            'position_key' => $data['position_key'] ?? null,
            'job_title_key' => $data['job_title_key'] ?? null,
            'work_location_key' => $data['work_location_key'] ?? null,
            'employment_type_key' => $data['employment_type_key'] ?? null,
            'employee_status' => $data['employee_status'] ?? null,
            'manager_user_id' => $data['manager_user_id'] ?? null,
            'display_name' => $data['display_name'] ?? null,
        ], fn ($v) => $v !== null);

        // json_extra 업데이트 (스칼라 값)
        $jsonExtraKeys = ['hire_date', 'resign_date', 'address', 'emergency_contact', 'salary', 'resident_number'];
        $extra = $employee->json_extra ?? [];
        foreach ($jsonExtraKeys as $key) {
            if (array_key_exists($key, $data)) {
                if ($data[$key] === null || $data[$key] === '') {
                    unset($extra[$key]);
                } else {
                    $extra[$key] = $data[$key];
                }
            }
        }

        // 급여이체정보 (bank_account 객체)
        if (array_key_exists('bank_account', $data)) {
            if (! empty($data['bank_account']) && is_array($data['bank_account'])) {
                $bankAccount = array_filter($data['bank_account'], fn ($v) => $v !== null && $v !== '');
                if (! empty($bankAccount)) {
                    $extra['bank_account'] = $bankAccount;
                } else {
                    unset($extra['bank_account']);
                }
            } else {
                unset($extra['bank_account']);
            }
        }

        // 부양가족 정보 (dependents 배열)
        if (array_key_exists('dependents', $data)) {
            if (! empty($data['dependents']) && is_array($data['dependents'])) {
                $dependents = array_values(array_filter($data['dependents'], function ($dep) {
                    return ! empty($dep['name']);
                }));
                if (! empty($dependents)) {
                    $extra['dependents'] = $dependents;
                } else {
                    unset($extra['dependents']);
                }
            } else {
                unset($extra['dependents']);
            }
        }

        $updateData['json_extra'] = ! empty($extra) ? $extra : null;

        $employee->update($updateData);

        // User 기본정보 동기화
        if ($employee->user) {
            $userUpdate = [];
            if (! empty($data['name'])) {
                $userUpdate['name'] = $data['name'];
            }
            if (! empty($data['email'])) {
                $userUpdate['email'] = $data['email'];
            }
            if (! empty($data['phone'])) {
                $userUpdate['phone'] = $data['phone'];
            }
            if (! empty($userUpdate)) {
                $employee->user->update($userUpdate);
            }
        }

        return $employee->fresh(['user', 'department']);
    }

    /**
     * 사원 삭제 (퇴직 처리)
     */
    public function deleteEmployee(int $id): bool
    {
        $employee = $this->getEmployeeById($id);
        if (! $employee) {
            return false;
        }

        $employee->update(['employee_status' => 'resigned']);

        return true;
    }

    /**
     * 부서 목록 (드롭다운용)
     */
    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 getPositions(string $type = 'rank'): \Illuminate\Database\Eloquent\Collection
    {
        return Position::query()
            ->forTenant()
            ->where('type', $type)
            ->where('is_active', true)
            ->ordered()
            ->get(['id', 'key', 'name']);
    }

    /**
     * 입퇴사자 현황 조회 (페이지네이션)
     */
    public function getEmployeeTenure(array $filters = [], int $perPage = 50): LengthAwarePaginator
    {
        $tenantId = session('selected_tenant_id');

        $query = Employee::query()
            ->with(['user', 'department'])
            ->forTenant($tenantId)
            ->whereNotNull('json_extra->hire_date');

        // 이름 검색
        if (! empty($filters['q'])) {
            $search = $filters['q'];
            $query->where(function ($q) use ($search) {
                $q->where('display_name', 'like', "%{$search}%")
                    ->orWhereHas('user', function ($uq) use ($search) {
                        $uq->where('name', 'like', "%{$search}%");
                    });
            });
        }

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

        // 상태 필터
        if (! empty($filters['status'])) {
            if ($filters['status'] === 'active') {
                $query->where('employee_status', 'active');
            } elseif ($filters['status'] === 'resigned') {
                $query->where('employee_status', 'resigned');
            }
        }

        // 입사기간 범위
        if (! empty($filters['hire_from'])) {
            $query->where('json_extra->hire_date', '>=', $filters['hire_from']);
        }
        if (! empty($filters['hire_to'])) {
            $query->where('json_extra->hire_date', '<=', $filters['hire_to']);
        }

        // 정렬
        $sortBy = $filters['sort_by'] ?? 'hire_date_desc';
        switch ($sortBy) {
            case 'hire_date_asc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) ASC");
                break;
            case 'tenure_desc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) ASC");
                break;
            case 'tenure_asc':
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) DESC");
                break;
            default: // hire_date_desc
                $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) DESC");
                break;
        }

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

    /**
     * 입퇴사자 통계
     */
    public function getTenureStats(): array
    {
        $tenantId = session('selected_tenant_id');

        $baseQuery = Employee::query()
            ->forTenant($tenantId)
            ->whereNotNull('json_extra->hire_date');

        $total = (clone $baseQuery)->count();
        $active = (clone $baseQuery)->where('employee_status', 'active')->count();
        $resigned = (clone $baseQuery)->where('employee_status', 'resigned')->count();

        // 올해 입사자
        $thisYear = now()->year;
        $hiredThisYear = (clone $baseQuery)
            ->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) LIKE ?", ["{$thisYear}%"])
            ->count();

        // 올해 퇴사자
        $resignedThisYear = Employee::query()
            ->forTenant($tenantId)
            ->whereNotNull('json_extra->resign_date')
            ->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.resign_date')) LIKE ?", ["{$thisYear}%"])
            ->count();

        // 평균 근속기간 (재직자 기준)
        $activeEmployees = (clone $baseQuery)
            ->where('employee_status', 'active')
            ->get(['json_extra']);

        $avgTenureDays = 0;
        if ($activeEmployees->isNotEmpty()) {
            $totalDays = 0;
            $count = 0;
            foreach ($activeEmployees as $emp) {
                $hireDate = $emp->json_extra['hire_date'] ?? null;
                if ($hireDate) {
                    $totalDays += Carbon::parse($hireDate)->diffInDays(today());
                    $count++;
                }
            }
            $avgTenureDays = $count > 0 ? (int) round($totalDays / $count) : 0;
        }

        return [
            'total' => $total,
            'active' => $active,
            'resigned' => $resigned,
            'hired_this_year' => $hiredThisYear,
            'resigned_this_year' => $resignedThisYear,
            'avg_tenure_days' => $avgTenureDays,
            'avg_tenure_label' => $this->formatTenure($avgTenureDays),
        ];
    }

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

        $query = Employee::query()
            ->with(['user', 'department'])
            ->forTenant($tenantId)
            ->whereNotNull('json_extra->hire_date');

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

        if (! empty($filters['status'])) {
            if ($filters['status'] === 'active') {
                $query->where('employee_status', 'active');
            } elseif ($filters['status'] === 'resigned') {
                $query->where('employee_status', 'resigned');
            }
        }

        if (! empty($filters['hire_from'])) {
            $query->where('json_extra->hire_date', '>=', $filters['hire_from']);
        }
        if (! empty($filters['hire_to'])) {
            $query->where('json_extra->hire_date', '<=', $filters['hire_to']);
        }

        return $query->orderByRaw("JSON_UNQUOTE(JSON_EXTRACT(json_extra, '$.hire_date')) DESC")->get();
    }

    /**
     * 근속일수 → "N년 M개월 D일" 변환
     */
    public function formatTenure(int $days): string
    {
        if ($days <= 0) {
            return '0일';
        }

        $years = intdiv($days, 365);
        $remaining = $days % 365;
        $months = intdiv($remaining, 30);
        $d = $remaining % 30;

        $parts = [];
        if ($years > 0) {
            $parts[] = "{$years}년";
        }
        if ($months > 0) {
            $parts[] = "{$months}개월";
        }
        if ($d > 0 || empty($parts)) {
            $parts[] = "{$d}일";
        }

        return implode(' ', $parts);
    }

    /**
     * 직급/직책 추가
     */
    public function createPosition(string $type, string $name): Position
    {
        $tenantId = session('selected_tenant_id');

        // key 생성: 이름을 소문자+언더스코어로 변환, 한글은 그대로
        $key = str_replace(' ', '_', mb_strtolower(trim($name)));

        // 중복 체크 후 존재하면 기존 반환
        $existing = Position::query()
            ->forTenant()
            ->where('type', $type)
            ->where('key', $key)
            ->first();

        if ($existing) {
            return $existing;
        }

        // 다음 sort_order
        $maxSort = Position::query()
            ->forTenant()
            ->where('type', $type)
            ->max('sort_order') ?? 0;

        return Position::create([
            'tenant_id' => $tenantId,
            'type' => $type,
            'key' => $key,
            'name' => trim($name),
            'sort_order' => $maxSort + 1,
            'is_active' => true,
        ]);
    }
}
