<?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;
use Illuminate\Support\Str;

class ArchiveService
{
    /**
     * 모델 클래스 → record_type 매핑
     */
    private const RECORD_TYPE_MAP = [
        Tenant::class => 'tenant',
        User::class => 'user',
        Department::class => 'department',
        Menu::class => 'menu',
        Role::class => 'role',
        // 추후 확장
        // Board::class => 'board',
        // Project::class => 'project',
        // Issue::class => 'issue',
        // Task::class => 'task',
    ];

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

    /**
     * 단일 모델 아카이브
     *
     * @param  Model  $model  아카이브할 모델
     * @param  array  $relationNames  함께 아카이브할 관계명 (예: ['users', 'departments'])
     * @param  string|null  $batchId  배치 ID (동일 배치로 묶을 경우)
     * @param  string|null  $description  배치 설명
     * @param  int|null  $tenantId  대상 테넌트 ID (명시적 지정)
     */
    public function archiveModel(
        Model $model,
        array $relationNames = [],
        ?string $batchId = null,
        ?string $description = null,
        ?int $tenantId = null
    ): ArchivedRecord {
        $batchId = $batchId ?? (string) Str::uuid();
        $recordType = $this->getRecordType($model);

        // 기본 설명 생성
        if (! $description) {
            $description = $this->generateDescription($model, $recordType);
        }

        // tenant_id 결정
        $tenantId = $tenantId ?? $this->determineTenantId($model, $recordType);

        return DB::transaction(function () use ($model, $relationNames, $batchId, $description, $recordType, $tenantId) {
            // 1. 메인 레코드 아카이브
            $archivedRecord = ArchivedRecord::create([
                'batch_id' => $batchId,
                'batch_description' => $description,
                'record_type' => $recordType,
                'tenant_id' => $tenantId,
                'original_id' => $model->getKey(),
                'main_data' => $model->toArray(),
                'schema_version' => 'v1.0',
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
                'created_by' => auth()->id(),
            ]);

            // 2. 관계 데이터 아카이브
            foreach ($relationNames as $relationName) {
                $this->archiveRelation($archivedRecord, $model, $relationName);
            }

            return $archivedRecord;
        });
    }

    /**
     * 배치 아카이브 (여러 모델을 하나의 배치로)
     *
     * @param  Collection  $models  아카이브할 모델 컬렉션
     * @param  string  $description  배치 설명
     * @param  array  $relationNames  각 모델에서 아카이브할 관계명
     * @return string 배치 ID
     */
    public function archiveBatch(
        Collection $models,
        string $description,
        array $relationNames = []
    ): string {
        $batchId = (string) Str::uuid();

        DB::transaction(function () use ($models, $description, $relationNames, $batchId) {
            foreach ($models as $model) {
                $this->archiveModel($model, $relationNames, $batchId, $description);
            }
        });

        return $batchId;
    }

    /**
     * 테넌트와 관련 데이터 전체 아카이브 (테넌트 삭제 전용)
     */
    public function archiveTenantWithRelations(Tenant $tenant): string
    {
        $batchId = (string) Str::uuid();
        $description = "테넌트 삭제: {$tenant->company_name} (ID: {$tenant->id})";

        DB::transaction(function () use ($tenant, $batchId, $description) {
            // 1. 메인 테넌트 아카이브 (tenant_id = 자기 자신)
            $archivedRecord = ArchivedRecord::create([
                'batch_id' => $batchId,
                'batch_description' => $description,
                'record_type' => 'tenant',
                'tenant_id' => $tenant->id,
                'original_id' => $tenant->id,
                'main_data' => $tenant->toArray(),
                'schema_version' => 'v1.0',
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
                'created_by' => auth()->id(),
            ]);

            // 2. 관련 사용자 아카이브 (user_tenants 피벗 포함)
            $users = $tenant->users()->get();
            if ($users->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'users',
                    'data' => $users->map(fn ($user) => [
                        'user' => $user->toArray(),
                        'pivot' => $user->pivot?->toArray(),
                    ])->toArray(),
                    'record_count' => $users->count(),
                    'created_by' => auth()->id(),
                ]);
            }

            // 3. 부서 아카이브
            $departments = $tenant->departments()->withTrashed()->get();
            if ($departments->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'departments',
                    'data' => $departments->toArray(),
                    'record_count' => $departments->count(),
                    'created_by' => auth()->id(),
                ]);
            }

            // 4. 메뉴 아카이브
            $menus = $tenant->menus()->withTrashed()->get();
            if ($menus->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'menus',
                    'data' => $menus->toArray(),
                    'record_count' => $menus->count(),
                    'created_by' => auth()->id(),
                ]);
            }

            // 5. 역할 아카이브 (Role 모델에는 SoftDeletes 없음)
            $roles = $tenant->roles()->get();
            if ($roles->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'roles',
                    'data' => $roles->toArray(),
                    'record_count' => $roles->count(),
                    'created_by' => auth()->id(),
                ]);
            }
        });

        return $batchId;
    }

    /**
     * 사용자 아카이브 (사용자 삭제 전용)
     */
    public function archiveUser(User $user): string
    {
        $batchId = (string) Str::uuid();
        $description = "사용자 삭제: {$user->name} ({$user->email})";

        // tenant_id = 현재 선택된 테넌트
        $tenantId = session('selected_tenant_id');

        DB::transaction(function () use ($user, $batchId, $description, $tenantId) {
            // 1. 메인 사용자 아카이브
            $archivedRecord = ArchivedRecord::create([
                'batch_id' => $batchId,
                'batch_description' => $description,
                'record_type' => 'user',
                'tenant_id' => $tenantId,
                'original_id' => $user->id,
                'main_data' => $user->toArray(),
                'schema_version' => 'v1.0',
                'deleted_by' => auth()->id(),
                'deleted_at' => now(),
                'created_by' => auth()->id(),
            ]);

            // 2. 테넌트 관계 아카이브
            $tenants = $user->tenants()->get();
            if ($tenants->isNotEmpty()) {
                ArchivedRecordRelation::create([
                    'archived_record_id' => $archivedRecord->id,
                    'table_name' => 'user_tenants',
                    'data' => $tenants->map(fn ($tenant) => [
                        'tenant_id' => $tenant->id,
                        'tenant_name' => $tenant->company_name,
                        'pivot' => $tenant->pivot?->toArray(),
                    ])->toArray(),
                    'record_count' => $tenants->count(),
                    'created_by' => auth()->id(),
                ]);
            }
        });

        return $batchId;
    }

    /**
     * 모델에서 record_type 추출
     */
    public function getRecordType(Model $model): string
    {
        $class = get_class($model);

        return self::RECORD_TYPE_MAP[$class] ?? Str::snake(class_basename($class));
    }

    /**
     * record_type에서 모델 클래스 추출
     */
    public function getModelClass(string $recordType): ?string
    {
        return self::MODEL_CLASS_MAP[$recordType] ?? null;
    }

    /**
     * 관계 데이터 아카이브
     */
    private function archiveRelation(ArchivedRecord $archivedRecord, Model $model, string $relationName): void
    {
        if (! method_exists($model, $relationName)) {
            return;
        }

        $relation = $model->{$relationName}();

        // withTrashed가 있으면 soft deleted 포함
        if (method_exists($relation, 'withTrashed')) {
            $relatedData = $relation->withTrashed()->get();
        } else {
            $relatedData = $relation->get();
        }

        if ($relatedData->isEmpty()) {
            return;
        }

        ArchivedRecordRelation::create([
            'archived_record_id' => $archivedRecord->id,
            'table_name' => $relationName,
            'data' => $relatedData->toArray(),
            'record_count' => $relatedData->count(),
            'created_by' => auth()->id(),
        ]);
    }

    /**
     * 기본 설명 생성
     */
    private function generateDescription(Model $model, string $recordType): string
    {
        $name = $model->name ?? $model->company_name ?? $model->title ?? "ID: {$model->getKey()}";

        return match ($recordType) {
            'tenant' => "테넌트 삭제: {$name}",
            'user' => "사용자 삭제: {$name}",
            'department' => "부서 삭제: {$name}",
            'menu' => "메뉴 삭제: {$name}",
            'role' => "역할 삭제: {$name}",
            default => "{$recordType} 삭제: {$name}",
        };
    }

    /**
     * tenant_id 결정 로직
     * - tenant 삭제: 삭제되는 테넌트의 ID (자기 자신)
     * - user 삭제: session('selected_tenant_id') (현재 선택된 테넌트)
     * - department/menu/role 삭제: 해당 레코드의 tenant_id
     */
    private function determineTenantId(Model $model, string $recordType): ?int
    {
        return match ($recordType) {
            'tenant' => $model->getKey(),
            'user' => session('selected_tenant_id'),
            'department', 'menu', 'role' => $model->tenant_id ?? session('selected_tenant_id'),
            default => session('selected_tenant_id'),
        };
    }
}
