<?php

namespace App\Services;

use App\Mail\UserPasswordMail;
use App\Models\DepartmentUser;
use App\Models\User;
use App\Models\UserRole;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;

class UserService
{
    public function __construct(
        private readonly ArchiveService $archiveService
    ) {}

    /**
     * 사용자 목록 조회 (페이지네이션)
     */
    public function getUsers(array $filters = [], int $perPage = 15): LengthAwarePaginator
    {
        $tenantId = session('selected_tenant_id');
        $query = User::query()->withTrashed();

        // 슈퍼관리자 보호: 일반관리자는 슈퍼관리자를 볼 수 없음
        if (! auth()->user()?->is_super_admin) {
            $query->where('is_super_admin', false);
        }

        // 역할/부서/테넌트 관계 eager loading (테넌트별)
        if ($tenantId) {
            $query->with([
                'userRoles' => fn ($q) => $q->where('tenant_id', $tenantId)->with('role'),
                'departmentUsers' => fn ($q) => $q->where('tenant_id', $tenantId)->with('department'),
                'tenants',
            ]);
        } else {
            $query->with(['tenants']);
        }

        // 테넌트 필터링 (user_tenants pivot을 통한 필터링)
        if ($tenantId) {
            $query->whereHas('tenants', function ($q) use ($tenantId) {
                $q->where('tenants.id', $tenantId);
            });
        }

        // Soft Delete 필터
        if (isset($filters['trashed'])) {
            if ($filters['trashed'] === 'only') {
                $query->onlyTrashed();
            } elseif ($filters['trashed'] === 'with') {
                $query->withTrashed();
            }
        }

        // 검색 필터
        if (! empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%")
                    ->orWhere('phone', 'like', "%{$search}%")
                    ->orWhere('user_id', 'like', "%{$search}%");
            });
        }

        // 활성 상태 필터
        if (isset($filters['is_active'])) {
            $query->where('is_active', $filters['is_active']);
        }

        return $query->orderBy('created_at', 'desc')->paginate($perPage);
    }

    /**
     * 사용자 상세 조회
     */
    public function getUserById(int $id): ?User
    {
        return User::find($id);
    }

    /**
     * 사용자 생성
     * - 본사(HQ): 임의 비밀번호 생성 + 메일 발송
     * - 비본사: 입력된 비밀번호 사용 (메일 발송 안 함)
     */
    public function createUser(array $data): User
    {
        $tenantId = session('selected_tenant_id');

        // 비밀번호 처리: 입력된 비밀번호가 있으면 사용, 없으면 자동 생성
        $passwordProvided = ! empty($data['password']);
        if ($passwordProvided) {
            // 비본사: 입력된 비밀번호 사용
            $data['password'] = Hash::make($data['password']);
            $plainPassword = null; // 메일 발송하지 않음
        } else {
            // 본사: 임의 비밀번호 생성 (8자리 영문+숫자)
            $plainPassword = $this->generateRandomPassword();
            $data['password'] = Hash::make($plainPassword);
        }

        // password_confirmation은 User 모델의 fillable이 아니므로 제거
        unset($data['password_confirmation']);

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // 최초 로그인 시 비밀번호 변경 필요
        $data['must_change_password'] = true;

        // 생성자 정보
        $data['created_by'] = auth()->id();

        // 사용자 생성
        $user = User::create($data);

        // user_tenants pivot에 관계 추가
        if ($tenantId) {
            $user->tenants()->attach($tenantId, [
                'is_active' => true,
                'is_default' => true,
                'joined_at' => now(),
            ]);

            // 역할/부서 동기화
            $roleIds = $data['role_ids'] ?? [];
            $departmentIds = $data['department_ids'] ?? [];

            $this->syncRoles($user, $tenantId, $roleIds);
            $this->syncDepartments($user, $tenantId, $departmentIds);
        }

        // 본사만 비밀번호 안내 메일 발송 (비본사는 관리자가 직접 알려줌)
        if ($plainPassword !== null) {
            $this->sendPasswordMail($user, $plainPassword, true);
        }

        return $user;
    }

    /**
     * 비밀번호 초기화 (관리자용: 임의 비밀번호 생성 + 메일 발송)
     */
    public function resetPassword(int $id): bool
    {
        $user = $this->getUserById($id);
        if (! $user) {
            return false;
        }

        // 임의 비밀번호 생성
        $plainPassword = $this->generateRandomPassword();

        // 비밀번호 업데이트 + 비밀번호 변경 필요 플래그
        $user->password = Hash::make($plainPassword);
        $user->must_change_password = true;
        $user->updated_by = auth()->id();
        $user->save();

        // 비밀번호 초기화 안내 메일 발송
        $this->sendPasswordMail($user, $plainPassword, false);

        return true;
    }

    /**
     * 임의 비밀번호 생성 (8자리 영문+숫자 조합)
     */
    private function generateRandomPassword(int $length = 8): string
    {
        // 영문 대소문자 + 숫자 조합으로 가독성 좋은 비밀번호 생성
        // 혼동되는 문자 제외: 0, O, l, 1, I
        $chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789';

        return substr(str_shuffle(str_repeat($chars, 3)), 0, $length);
    }

    /**
     * 비밀번호 안내 메일 발송
     */
    private function sendPasswordMail(User $user, string $password, bool $isNewUser): void
    {
        Mail::to($user->email)->send(new UserPasswordMail($user, $password, $isNewUser));
    }

    /**
     * 사용자 수정
     */
    public function updateUser(int $id, array $data): bool
    {
        $user = $this->getUserById($id);
        if (! $user) {
            return false;
        }

        $tenantId = session('selected_tenant_id');

        // 비밀번호가 입력된 경우만 업데이트
        if (! empty($data['password'])) {
            $data['password'] = Hash::make($data['password']);
        } else {
            unset($data['password']);
        }

        // is_active 처리
        $data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';

        // is_super_admin 처리 (슈퍼관리자만 수정 가능하므로 validated 데이터에 있을 때만)
        if (array_key_exists('is_super_admin', $data)) {
            $data['is_super_admin'] = $data['is_super_admin'] == '1';
        }

        // 수정자 정보
        $data['updated_by'] = auth()->id();

        // 역할/부서 동기화 (테넌트가 선택된 경우)
        if ($tenantId) {
            $roleIds = $data['role_ids'] ?? [];
            $departmentIds = $data['department_ids'] ?? [];

            $this->syncRoles($user, $tenantId, $roleIds);
            $this->syncDepartments($user, $tenantId, $departmentIds);
        }

        // role_ids, department_ids는 User 모델의 fillable이 아니므로 제거
        unset($data['role_ids'], $data['department_ids']);

        return $user->update($data);
    }

    /**
     * 사용자 역할 동기화 (특정 테넌트)
     */
    public function syncRoles(User $user, int $tenantId, array $roleIds): void
    {
        // 기존 역할 삭제 (해당 테넌트만) - forceDelete로 실제 삭제
        UserRole::withTrashed()
            ->where('user_id', $user->id)
            ->where('tenant_id', $tenantId)
            ->forceDelete();

        // 새 역할 추가
        foreach ($roleIds as $roleId) {
            UserRole::create([
                'user_id' => $user->id,
                'tenant_id' => $tenantId,
                'role_id' => $roleId,
                'assigned_at' => now(),
            ]);
        }
    }

    /**
     * 사용자 부서 동기화 (특정 테넌트)
     */
    public function syncDepartments(User $user, int $tenantId, array $departmentIds): void
    {
        // 기존 부서 삭제 (해당 테넌트만) - forceDelete로 실제 삭제
        DepartmentUser::withTrashed()
            ->where('user_id', $user->id)
            ->where('tenant_id', $tenantId)
            ->forceDelete();

        // 새 부서 추가 (첫 번째를 primary로)
        foreach ($departmentIds as $index => $departmentId) {
            DepartmentUser::create([
                'user_id' => $user->id,
                'tenant_id' => $tenantId,
                'department_id' => $departmentId,
                'is_primary' => $index === 0,
                'joined_at' => now(),
                'created_by' => auth()->id(),
            ]);
        }
    }

    /**
     * 사용자 삭제 (Soft Delete)
     */
    public function deleteUser(int $id): bool
    {
        $user = $this->getUserById($id);
        if (! $user) {
            return false;
        }

        $user->deleted_by = auth()->id();
        $user->save();

        return $user->delete();
    }

    /**
     * 사용자 복원
     */
    public function restoreUser(int $id): bool
    {
        $user = User::onlyTrashed()->findOrFail($id);

        return $user->restore();
    }

    /**
     * 사용자 영구 삭제 (슈퍼관리자 전용)
     *
     * 1. 사용자 데이터를 아카이브에 저장
     * 2. 관련 데이터 삭제
     * 3. 사용자 영구 삭제
     */
    public function forceDeleteUser(int $id): bool
    {
        $user = User::withTrashed()->findOrFail($id);

        return DB::transaction(function () use ($user) {
            // 1. 아카이브에 저장 (복원 가능하도록)
            $this->archiveService->archiveUser($user);

            // 2. 관련 데이터 삭제
            $user->tenants()->detach(); // user_tenants 관계 삭제

            // 2-1. user_roles 영구 삭제 (외래 키 제약 때문에 forceDelete 필요)
            DB::table('user_roles')->where('user_id', $user->id)->delete();

            // 2-2. department_user 영구 삭제
            DB::table('department_user')->where('user_id', $user->id)->delete();

            // 2-3. sales_partners 삭제 (영업파트너)
            DB::table('sales_partners')->where('user_id', $user->id)->delete();

            // 2-4. sales_manager_documents 삭제 (영업파트너 서류)
            DB::table('sales_manager_documents')->where('user_id', $user->id)->delete();

            // 2-5. 하위 사용자의 parent_id 해제
            User::where('parent_id', $user->id)->update(['parent_id' => null]);

            // 3. 사용자 영구 삭제
            return $user->forceDelete();
        });
    }

    /**
     * 모달용 사용자 상세 정보 조회
     */
    public function getUserForModal(int $id): ?User
    {
        $tenantId = session('selected_tenant_id');

        $query = User::query()
            ->with('deletedByUser')
            ->withTrashed();

        // 역할/부서 관계 eager loading (테넌트별)
        if ($tenantId) {
            $query->with([
                'userRoles' => fn ($q) => $q->where('tenant_id', $tenantId)->with('role'),
                'departmentUsers' => fn ($q) => $q->where('tenant_id', $tenantId)->with('department'),
                'tenants',
            ]);
        } else {
            $query->with(['userRoles.role', 'departmentUsers.department', 'tenants']);
        }

        $user = $query->find($id);

        // 권한 카운트 추가
        if ($user && $tenantId) {
            $permissionCounts = $this->getUserPermissionCounts($user->id, $tenantId);
            $user->web_permission_count = $permissionCounts['web'];
            $user->api_permission_count = $permissionCounts['api'];
        }

        return $user;
    }

    /**
     * 사용자별 guard별 권한 개수 조회 (역할 + 부서 + 개인 오버라이드 통합)
     */
    private function getUserPermissionCounts(int $userId, int $tenantId): array
    {
        $result = ['web' => 0, 'api' => 0];
        $now = now();

        foreach (['web', 'api'] as $guardName) {
            // 1. 역할 권한
            $rolePermissions = \DB::table('model_has_roles as mhr')
                ->join('role_has_permissions as rhp', 'rhp.role_id', '=', 'mhr.role_id')
                ->join('permissions as p', 'p.id', '=', 'rhp.permission_id')
                ->where('mhr.model_type', User::class)
                ->where('mhr.model_id', $userId)
                ->where('p.guard_name', $guardName)
                ->where('p.name', 'like', 'menu:%')
                ->pluck('p.name')
                ->toArray();

            // 2. 부서 권한
            $deptPermissions = \DB::table('department_user as du')
                ->join('permission_overrides as po', function ($j) use ($now, $tenantId) {
                    $j->on('po.model_id', '=', 'du.department_id')
                        ->where('po.model_type', 'App\\Models\\Tenants\\Department')
                        ->where('po.tenant_id', $tenantId)
                        ->whereNull('po.deleted_at')
                        ->where('po.effect', 1)
                        ->where(function ($w) use ($now) {
                            $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
                        })
                        ->where(function ($w) use ($now) {
                            $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
                        });
                })
                ->join('permissions as p', 'p.id', '=', 'po.permission_id')
                ->whereNull('du.deleted_at')
                ->where('du.user_id', $userId)
                ->where('du.tenant_id', $tenantId)
                ->where('p.guard_name', $guardName)
                ->where('p.name', 'like', 'menu:%')
                ->pluck('p.name')
                ->toArray();

            // 3. 개인 오버라이드 (ALLOW)
            $personalAllows = \DB::table('permission_overrides as po')
                ->join('permissions as p', 'p.id', '=', 'po.permission_id')
                ->where('po.model_type', User::class)
                ->where('po.model_id', $userId)
                ->where('po.tenant_id', $tenantId)
                ->where('po.effect', 1)
                ->whereNull('po.deleted_at')
                ->where(function ($w) use ($now) {
                    $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
                })
                ->where(function ($w) use ($now) {
                    $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
                })
                ->where('p.guard_name', $guardName)
                ->where('p.name', 'like', 'menu:%')
                ->pluck('p.name')
                ->toArray();

            // 4. 개인 오버라이드 (DENY) - 제외할 권한
            $personalDenies = \DB::table('permission_overrides as po')
                ->join('permissions as p', 'p.id', '=', 'po.permission_id')
                ->where('po.model_type', User::class)
                ->where('po.model_id', $userId)
                ->where('po.tenant_id', $tenantId)
                ->where('po.effect', 0)
                ->whereNull('po.deleted_at')
                ->where(function ($w) use ($now) {
                    $w->whereNull('po.effective_from')->orWhere('po.effective_from', '<=', $now);
                })
                ->where(function ($w) use ($now) {
                    $w->whereNull('po.effective_to')->orWhere('po.effective_to', '>=', $now);
                })
                ->where('p.guard_name', $guardName)
                ->where('p.name', 'like', 'menu:%')
                ->pluck('p.name')
                ->toArray();

            // 통합: (역할 OR 부서 OR 개인ALLOW) - 개인DENY
            $allAllowed = array_unique(array_merge($rolePermissions, $deptPermissions, $personalAllows));
            $effectivePermissions = array_diff($allAllowed, $personalDenies);

            $result[$guardName] = count($effectivePermissions);
        }

        return $result;
    }

    /**
     * 활성 사용자 목록 조회 (드롭다운용)
     */
    public function getActiveUsers()
    {
        $tenantId = session('selected_tenant_id');
        $query = User::query()->where('is_active', true);

        // 슈퍼관리자 보호: 일반관리자는 슈퍼관리자를 볼 수 없음
        if (! auth()->user()?->is_super_admin) {
            $query->where('is_super_admin', false);
        }

        // 테넌트 필터링 (user_tenants pivot을 통한 필터링)
        if ($tenantId) {
            $query->whereHas('tenants', function ($q) use ($tenantId) {
                $q->where('tenants.id', $tenantId);
            });
        }

        return $query->orderBy('name')->get();
    }

    /**
     * 슈퍼관리자 보호: 일반관리자가 슈퍼관리자에 접근할 수 있는지 확인
     *
     * @param  int  $targetUserId  대상 사용자 ID
     * @return bool true면 접근 가능, false면 접근 불가
     */
    public function canAccessUser(int $targetUserId): bool
    {
        // withTrashed()를 사용하여 soft-deleted 사용자도 확인 (복원 시 필요)
        $targetUser = User::withTrashed()->find($targetUserId);
        $currentUser = auth()->user();

        // 대상 사용자가 슈퍼관리자이고 현재 사용자가 슈퍼관리자가 아니면 접근 불가
        if ($targetUser?->is_super_admin && ! $currentUser?->is_super_admin) {
            return false;
        }

        return true;
    }
}
