<?php

namespace App\Services;

use App\Models\Members\User;
use App\Models\Members\UserTenant;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class AdminService
{
    /**
     * [GET] 테넌트 사용자 목록
     * - 컨트롤러 index()에서 호출
     * - 검색/정렬/페이징 최소 항목 포함
     */
    public static function getTenants(array $params = [])
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '성 테넌트가 없습니다.', 'code' => 400];
        }

        $page = isset($params['page']) ? (int) $params['page'] : 1;
        $size = isset($params['size']) ? (int) $params['size'] : 10;
        $keyword = $params['q'] ?? null;
        $active = $params['is_active'] ?? null; // 0/1
        $sortBy = $params['sort_by'] ?? 'users.id';
        $sortDir = strtolower($params['sort_dir'] ?? 'desc') === 'asc' ? 'asc' : 'desc';

        $q = UserTenant::query()
            ->with(['user:id,name,email,phone'])
            ->where('tenant_id', $tenantId);

        if ($keyword) {
            $q->whereHas('user', function ($sub) use ($keyword) {
                $sub->where(function ($w) use ($keyword) {
                    $w->where('name', 'like', "%{$keyword}%")
                        ->orWhere('email', 'like', "%{$keyword}%")
                        ->orWhere('phone', 'like', "%{$keyword}%");
                });
            });
        }

        if ($active !== null && $active !== '') {
            $q->where('user_tenants.is_active', (int) $active);
        }

        // 조인 정렬용
        $q->leftJoin('users', 'users.id', '=', 'user_tenants.user_id')
            ->select(
                'users.id',
                'users.user_id',
                'users.name',
                'users.email',
                'users.phone',
                'user_tenants.is_active',
                'user_tenants.joined_at',
                'user_tenants.left_at',
                'user_tenants.tenant_id'
            );

        $q->orderBy($sortBy, $sortDir);

        return $data = $q->paginate($size, ['*'], 'page', $page);
    }

    /**
     * [POST] 테넌트 사용자 추가 (기존 사용자 연결)
     * - 컨트롤러 store()에서 호출
     * - 유저 등록 역할 부여
     */
    public static function store(array $params = [])
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }

        // 신규 회원 생성 + 역할 부여 지원
        $v = Validator::make($params, [
            'user_id' => 'required|string|max:255|unique:users,user_id',
            'name' => 'required|string|max:255',
            'email' => 'required|email|max:100|unique:users,email',
            'phone' => 'nullable|string|max:30',
            'password' => 'required|string|min:8|max:64',
            'roles' => 'nullable|array',
            'roles.*' => 'string|max:100', // 각각의 역할 이름
        ]);

        if ($v->fails()) {
            return ['error' => $v->errors()->first(), 'code' => 422];
        }

        $payload = $v->validated();

        return DB::transaction(function () use ($payload, $tenantId) {
            // 신규 사용자 생성
            $user = User::create([
                'user_id' => $payload['user_id'],
                'name' => $payload['name'],
                'email' => $payload['email'],
                'phone' => $payload['phone'] ?? null,
                'password' => $payload['password'], // 캐스트가 알아서 해싱
            ]);

            // 현재 테넌트에 활성 연결
            UserTenant::create([
                'user_id' => $user->id,
                'tenant_id' => $tenantId,
                'is_active' => 1,
                'is_default' => 0,
                'joined_at' => now(),
            ]);

            // 역할 부여 (Spatie Permission teams 모드 가정)
            if (! empty($payload['roles']) && method_exists($user, 'assignRole')) {
                $previousTeam = app()->has('permission.team_id') ? app('permission.team_id') : null;
                app()->instance('permission.team_id', $tenantId);

                try {
                    foreach ($payload['roles'] as $roleName) {
                        $user->assignRole($roleName);
                    }
                } finally {
                    app()->instance('permission.team_id', $previousTeam);
                }
            }

            return [
                'user' => $user->only(['id', 'user_id', 'name', 'email', 'phone']),
            ];
        });
    }

    /**
     * [GET] 테넌트 사용자 단건 조회
     * - 컨트롤러 show()에서 호출
     */
    public static function show(int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }

        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        $user = User::whereHas('userTenants')->find($userNo);

        if (! $user) {
            return ['error' => '해당 사용자를 찾을 수 없습니다.', 'code' => 404];
        }

        return $user;
    }

    /**
     * [PUT/PATCH] 테넌트 사용자 정보 수정
     * - 회원 기본정보(user_id, name, email, phone, password) 변경
     * - 역할(roles) 변경 및 삭제 처리
     */
    public static function update(array $params, int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }
        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        // 1) 유저 존재/테넌트 소속 확인
        $user = User::find($userNo);
        if (! $user) {
            return ['error' => '해당 회원을 찾을 수 없습니다.', 'code' => 404];
        }
        $linked = UserTenant::where('tenant_id', $tenantId)
            ->where('user_id', $userNo)
            ->exists();
        if (! $linked) {
            return ['error' => '이 테넌트에 소속된 회원이 아닙니다.', 'code' => 403];
        }

        // 2) 프로필 + roles만 수정
        $v = Validator::make($params, [
            'user_id' => ['nullable', 'string', 'max:255', Rule::unique('users', 'user_id')->ignore($userNo)],
            'name' => 'nullable|string|max:255',
            'email' => ['nullable', 'email', 'max:100', Rule::unique('users', 'email')->ignore($userNo)],
            'phone' => 'nullable|string|max:30',
            'password' => 'nullable|string|min:8|max:64',

            'roles' => 'nullable|array',
            'roles.*' => 'string|max:100',
        ]);
        if ($v->fails()) {
            return ['error' => $v->errors()->first(), 'code' => 422];
        }
        $payload = $v->validated();

        // 아무 필드도 없으면 방어
        $updatableKeys = ['user_id', 'name', 'email', 'phone', 'password'];
        $hasProfileInput = (bool) array_intersect(array_keys($payload), $updatableKeys);
        $hasRolesInput = array_key_exists('roles', $payload);
        if (! $hasProfileInput && ! $hasRolesInput) {
            return ['error' => '수정할 항목이 없습니다.', 'code' => 422];
        }

        return DB::transaction(function () use ($user, $payload, $tenantId, $updatableKeys) {

            // 3) 프로필 업데이트 (제공된 키만 반영)
            $updateData = [];
            foreach ($updatableKeys as $k) {
                if (array_key_exists($k, $payload)) {
                    $updateData[$k] = $payload[$k];
                }
            }

            // 비밀번호 처리
            if (array_key_exists('password', $updateData)) {
                if ($updateData['password'] === null || $updateData['password'] === '') {
                    unset($updateData['password']); // 빈 값 들어오면 무시
                }
            }

            if (! empty($updateData)) {
                $user->fill($updateData);
                $user->save();
            }

            // 4) 역할 수정 (teams 모드: 테넌트 컨텍스트로 sync)
            if (array_key_exists('roles', $payload) && method_exists($user, 'syncRoles')) {
                $previousTeam = app()->has('permission.team_id') ? app('permission.team_id') : null;
                app()->instance('permission.team_id', $tenantId);
                try {
                    // roles 키가 있으면 그 값으로 덮어쓰기 (빈 배열이면 모두 제거)
                    $roles = $payload['roles'] ?? [];
                    $user->syncRoles($roles);
                } finally {
                    app()->instance('permission.team_id', $previousTeam);
                }
            }

            return [
                'user' => $user->only(['id', 'user_id', 'name', 'email', 'phone']),
                'roles' => method_exists($user, 'getRoleNames') ? $user->getRoleNames() : [],
            ];
        });
    }

    /**
     * [DELETE] 테넌트 사용자 삭제(연결 해제)
     * - soft delete + left_at 기록
     */
    public static function destroy(int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }
        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        $ut = UserTenant::where('user_id', $userNo)
            ->where('tenant_id', $tenantId)
            ->first();

        if (! $ut) {
            return ['error' => '해당 사용자를 찾을 수 없습니다.', 'code' => 404];
        }

        $ut->left_at = now();
        $ut->save();
        $ut->delete(); // SoftDeletes 가정

        return 'success';
    }

    /**
     * [POST] 삭제 복구
     */
    public static function restore(int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }
        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        $ut = UserTenant::withTrashed()
            ->where('tenant_id', $tenantId)
            ->where('user_id', $userNo)
            ->first();

        if (! $ut) {
            return ['error' => '해당 사용자를 찾을 수 없습니다.', 'code' => 404];
        }

        if ($ut->trashed()) {
            $ut->restore();
            $ut->left_at = null;
            $ut->save();
        }

        return 'success';
    }

    /**
     * [PATCH] 활성/비활성 토글
     */
    public static function toggle(int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }
        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        $ut = UserTenant::where('tenant_id', $tenantId)
            ->where('user_id', $userNo)
            ->first();

        if (! $ut) {
            return ['error' => '해당 사용자를 찾을 수 없습니다.', 'code' => 404];
        }

        $ut->is_active = $ut->is_active ? 0 : 1;
        $ut->save();

        return ['is_active' => $ut->is_active];
    }

    /**
     * [POST] 역할 부여 (Spatie Permission - teams 사용 가정)
     * - params: user_id, role_name
     */
    public static function attach(array $params = [])
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }

        $v = Validator::make($params, [
            'user_id' => 'required|integer|exists:users,id',
            'role_name' => 'required|string|max:100',
        ]);
        if ($v->fails()) {
            return ['error' => $v->errors()->first(), 'code' => 422];
        }

        $user = User::find($params['user_id']);
        if (! method_exists($user, 'assignRole')) {
            // Spatie 미사용 환경 방어
            return ['error' => '역할 시스템이 활성화되어 있지 않습니다.', 'code' => 501];
        }

        // teams(tenant) 스코프
        $previousTeam = app()->has('permission.team_id') ? app('permission.team_id') : null;
        app()->instance('permission.team_id', $tenantId);

        try {
            $user->assignRole($params['role_name']);
        } finally {
            // 원복
            app()->instance('permission.team_id', $previousTeam);
        }

        return 'success';
    }

    /**
     * [POST] 역할 해제 (Spatie Permission - teams 사용 가정)
     * - params: user_id, role_name
     */
    public static function detach(array $params = [])
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }

        $v = Validator::make($params, [
            'user_id' => 'required|integer|exists:users,id',
            'role_name' => 'required|string|max:100',
        ]);
        if ($v->fails()) {
            return ['error' => $v->errors()->first(), 'code' => 422];
        }

        $user = User::find($params['user_id']);
        if (! method_exists($user, 'removeRole')) {
            return ['error' => '역할 시스템이 활성화되어 있지 않습니다.', 'code' => 501];
        }

        $previousTeam = app()->has('permission.team_id') ? app('permission.team_id') : null;
        app()->instance('permission.team_id', $tenantId);

        try {
            $user->removeRole($params['role_name']);
        } finally {
            app()->instance('permission.team_id', $previousTeam);
        }

        return 'success';
    }

    /**
     * [POST] 테넌트 사용자 비밀번호 초기화
     * - (보안) 관리자 권한 확인은 미들웨어/가드에서 처리 가정
     * - 새 임시 비밀번호를 설정(응답으로 직접 노출 X 권장)
     * - 여기서는 옵션에 따라 노출/미노출 선택 가능하도록 구현
     */
    public static function reset(array $params, int $userNo)
    {
        $tenantId = app('tenant_id');
        if (! $tenantId) {
            return ['error' => '활성 테넌트가 없습니다.', 'code' => 400];
        }
        if (! $userNo) {
            return ['error' => '회원 정보가 없습니다.', 'code' => 422];
        }

        $v = Validator::make($params, [
            'new_password' => 'nullable|string|min:8|max:64',
            'return_password' => 'nullable|in:0,1', // 1이면 응답에 임시 비번 포함(개발용)
        ]);
        if ($v->fails()) {
            return ['error' => $v->errors()->first(), 'code' => 422];
        }
        $payload = $v->validated();

        $user = User::find($userNo);
        if (! $user) {
            return ['error' => '유저를 찾을 수 없습니다.', 'code' => 404];
        }

        $new = $payload['new_password'] ?? Str::random(12);
        $user->password = $new;
        $user->save();

        // (선택) 기존 토큰 무효화
        // if (method_exists($user, 'tokens')) { $user->tokens()->delete(); }

        $resp = ['status' => 'ok'];
        if (! empty($payload['return_password'])) {
            // 운영에선 반환하지 말고 메일/문자 발송을 권장
            $resp['temp_password'] = $new;
        }

        return $resp;
    }
}
