<?php

namespace App\Services;

use App\Models\Archives\ArchivedRecord;
use App\Models\Archives\ArchivedRecordRelation;
use App\Models\Commons\Menu;
use App\Models\Department;
use App\Models\Role;
use App\Models\Tenants\Tenant;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class RestoreService
{
    /**
     * record_type → 모델 클래스 매핑
     */
    private const MODEL_CLASS_MAP = [
        'tenant' => Tenant::class,
        'user' => User::class,
        'department' => Department::class,
        'menu' => Menu::class,
        'role' => Role::class,
    ];

    /**
     * record_type → 테이블명 매핑
     */
    private const TABLE_NAME_MAP = [
        'tenant' => 'tenants',
        'user' => 'users',
        'department' => 'departments',
        'menu' => 'menus',
        'role' => 'roles',
    ];

    /**
     * 단일 아카이브 레코드 복원
     *
     * @return Model 복원된 모델
     *
     * @throws \Exception 복원 실패 시
     */
    public function restoreRecord(ArchivedRecord $record): Model
    {
        $modelClass = $this->getModelClass($record->record_type);

        if (! $modelClass) {
            throw new \Exception("지원하지 않는 레코드 타입: {$record->record_type}");
        }

        return DB::transaction(function () use ($record, $modelClass) {
            // 1. 메인 데이터 복원
            $mainData = $record->main_data;

            // ID, timestamps 제거 (새로 생성되도록)
            unset($mainData['id'], $mainData['created_at'], $mainData['updated_at'], $mainData['deleted_at']);

            // soft delete 관련 필드 초기화
            $mainData['deleted_by'] = null;

            // 복원자 정보 추가
            $mainData['created_by'] = auth()->id();
            $mainData['updated_by'] = auth()->id();

            // 모델 생성
            $model = $modelClass::create($mainData);

            // 2. 아카이브 레코드 삭제
            $record->relations()->delete();
            $record->delete();

            return $model;
        });
    }

    /**
     * 배치 전체 복원 (테넌트 + 관련 데이터)
     *
     * @return Collection 복원된 모델들
     */
    public function restoreBatch(string $batchId): Collection
    {
        $records = ArchivedRecord::where('batch_id', $batchId)
            ->with('relations')
            ->get();

        if ($records->isEmpty()) {
            throw new \Exception("배치를 찾을 수 없습니다: {$batchId}");
        }

        $restoredModels = collect();

        DB::transaction(function () use ($records, &$restoredModels) {
            foreach ($records as $record) {
                // 테넌트 복원의 경우 특별 처리
                if ($record->record_type === 'tenant') {
                    $restoredModels->push($this->restoreTenantWithRelations($record));
                } elseif ($record->record_type === 'user') {
                    $restoredModels->push($this->restoreUserWithRelations($record));
                } else {
                    $restoredModels->push($this->restoreRecord($record));
                }
            }
        });

        return $restoredModels;
    }

    /**
     * 테넌트와 관련 데이터 복원
     */
    public function restoreTenantWithRelations(ArchivedRecord $record): Tenant
    {
        if ($record->record_type !== 'tenant') {
            throw new \Exception('테넌트 타입의 레코드가 아닙니다.');
        }

        return DB::transaction(function () use ($record) {
            // 1. 테넌트 복원
            $tenantData = $record->main_data;
            unset($tenantData['id'], $tenantData['created_at'], $tenantData['updated_at'], $tenantData['deleted_at']);
            $tenantData['deleted_by'] = null;
            $tenantData['created_by'] = auth()->id();

            $tenant = Tenant::create($tenantData);

            // 2. 관련 데이터 복원
            foreach ($record->relations as $relation) {
                $this->restoreRelationData($tenant, $relation);
            }

            // 3. 아카이브 삭제
            $record->relations()->delete();
            $record->delete();

            return $tenant;
        });
    }

    /**
     * 사용자와 관련 데이터 복원
     */
    public function restoreUserWithRelations(ArchivedRecord $record): User
    {
        if ($record->record_type !== 'user') {
            throw new \Exception('사용자 타입의 레코드가 아닙니다.');
        }

        return DB::transaction(function () use ($record) {
            // 1. 사용자 복원
            $userData = $record->main_data;
            unset($userData['id'], $userData['created_at'], $userData['updated_at'], $userData['deleted_at']);
            $userData['deleted_by'] = null;
            $userData['created_by'] = auth()->id();

            $user = User::create($userData);

            // 2. 테넌트 관계 복원
            foreach ($record->relations as $relation) {
                if ($relation->table_name === 'user_tenants') {
                    foreach ($relation->data as $tenantData) {
                        // 테넌트가 존재하는지 확인
                        $tenantId = $tenantData['tenant_id'] ?? null;
                        if ($tenantId && Tenant::find($tenantId)) {
                            $pivotData = $tenantData['pivot'] ?? [];
                            unset($pivotData['user_id'], $pivotData['tenant_id']);
                            $user->tenants()->attach($tenantId, $pivotData);
                        }
                    }
                }
            }

            // 3. 아카이브 삭제
            $record->relations()->delete();
            $record->delete();

            return $user;
        });
    }

    /**
     * 관계 데이터 복원
     */
    private function restoreRelationData(Tenant $tenant, ArchivedRecordRelation $relation): void
    {
        $tableName = $relation->table_name;
        $data = $relation->data;

        switch ($tableName) {
            case 'users':
                // 사용자 복원은 별도 처리 (user_tenants 피벗 포함)
                foreach ($data as $item) {
                    $userData = $item['user'] ?? $item;
                    $pivotData = $item['pivot'] ?? [];

                    // 이미 존재하는 사용자인지 확인
                    $existingUser = User::where('email', $userData['email'])->first();
                    if ($existingUser) {
                        // 기존 사용자에 테넌트 관계만 추가
                        if (! $existingUser->tenants()->where('tenants.id', $tenant->id)->exists()) {
                            unset($pivotData['user_id'], $pivotData['tenant_id']);
                            $existingUser->tenants()->attach($tenant->id, $pivotData);
                        }
                    }
                    // 새 사용자 생성은 보안상 하지 않음 (비밀번호 문제)
                }
                break;

            case 'departments':
                foreach ($data as $deptData) {
                    unset($deptData['id'], $deptData['created_at'], $deptData['updated_at'], $deptData['deleted_at']);
                    $deptData['tenant_id'] = $tenant->id;
                    $deptData['created_by'] = auth()->id();
                    Department::create($deptData);
                }
                break;

            case 'menus':
                // 메뉴는 parent_id 순서 고려 필요 (부모 먼저)
                $sortedMenus = collect($data)->sortBy('parent_id')->values();
                $menuIdMap = []; // 원본 ID → 새 ID 매핑

                foreach ($sortedMenus as $menuData) {
                    $originalId = $menuData['id'];
                    unset($menuData['id'], $menuData['created_at'], $menuData['updated_at'], $menuData['deleted_at']);
                    $menuData['tenant_id'] = $tenant->id;
                    $menuData['created_by'] = auth()->id();

                    // parent_id 매핑
                    if (! empty($menuData['parent_id']) && isset($menuIdMap[$menuData['parent_id']])) {
                        $menuData['parent_id'] = $menuIdMap[$menuData['parent_id']];
                    } else {
                        $menuData['parent_id'] = null;
                    }

                    $newMenu = Menu::create($menuData);
                    $menuIdMap[$originalId] = $newMenu->id;
                }
                break;

            case 'roles':
                foreach ($data as $roleData) {
                    unset($roleData['id'], $roleData['created_at'], $roleData['updated_at'], $roleData['deleted_at']);
                    $roleData['tenant_id'] = $tenant->id;
                    $roleData['created_by'] = auth()->id();
                    Role::create($roleData);
                }
                break;
        }
    }

    /**
     * record_type에서 모델 클래스 가져오기
     */
    private function getModelClass(string $recordType): ?string
    {
        return self::MODEL_CLASS_MAP[$recordType] ?? null;
    }

    /**
     * 복원 가능 여부 확인
     */
    public function canRestore(ArchivedRecord $record): array
    {
        $issues = [];

        // 1. 모델 클래스 지원 여부
        if (! $this->getModelClass($record->record_type)) {
            $issues[] = "지원하지 않는 레코드 타입입니다: {$record->record_type}";
        }

        // 2. 테넌트 복원 시, 동일 코드 존재 여부 확인
        if ($record->record_type === 'tenant') {
            $code = $record->main_data['code'] ?? null;
            if ($code && Tenant::where('code', $code)->exists()) {
                $issues[] = "동일한 테넌트 코드가 이미 존재합니다: {$code}";
            }
        }

        // 3. 사용자 복원 시, 동일 이메일 존재 여부 확인
        if ($record->record_type === 'user') {
            $email = $record->main_data['email'] ?? null;
            if ($email && User::where('email', $email)->exists()) {
                $issues[] = "동일한 이메일의 사용자가 이미 존재합니다: {$email}";
            }
        }

        return [
            'can_restore' => empty($issues),
            'issues' => $issues,
        ];
    }

    /**
     * 배치 내 모든 레코드의 복원 가능 여부 확인
     */
    public function canRestoreBatch(string $batchId): array
    {
        $records = ArchivedRecord::where('batch_id', $batchId)->get();
        $allIssues = [];

        foreach ($records as $record) {
            $check = $this->canRestore($record);
            if (! $check['can_restore']) {
                $allIssues = array_merge($allIssues, $check['issues']);
            }
        }

        return [
            'can_restore' => empty($allIssues),
            'issues' => array_unique($allIssues),
            'record_count' => $records->count(),
        ];
    }
}
