<?php

namespace App\Services;

use App\Models\Commons\Menu;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * 메뉴 동기화 서비스 (테넌트 관리자용)
 *
 * 동기화 상태:
 * - new: 글로벌에 있으나 테넌트에 없음
 * - up_to_date: 글로벌과 동일한 상태
 * - updatable: 글로벌이 변경됨, 동기화 가능
 * - customized: 테넌트가 수정함 (보호됨)
 * - deleted: 글로벌에서 삭제됨
 */
class MenuSyncService extends Service
{
    public const STATUS_NEW = 'new';

    public const STATUS_UP_TO_DATE = 'up_to_date';

    public const STATUS_UPDATABLE = 'updatable';

    public const STATUS_CUSTOMIZED = 'customized';

    public const STATUS_DELETED = 'deleted';

    /**
     * 동기화 상태 목록 조회
     */
    public function getSyncStatus(?string $statusFilter = null): array
    {
        $tenantId = $this->tenantId();

        // 글로벌 메뉴 조회
        $globalMenus = Menu::global()
            ->where('is_active', true)
            ->get()
            ->keyBy('id');

        // 테넌트 메뉴 조회 (global_menu_id가 있는 것만)
        $tenantMenus = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->whereNotNull('global_menu_id')
            ->get()
            ->keyBy('global_menu_id');

        $items = [];
        $summary = [
            self::STATUS_NEW => 0,
            self::STATUS_UP_TO_DATE => 0,
            self::STATUS_UPDATABLE => 0,
            self::STATUS_CUSTOMIZED => 0,
            self::STATUS_DELETED => 0,
        ];

        // 1. 글로벌 메뉴 기준으로 상태 확인
        foreach ($globalMenus as $globalMenu) {
            $tenantMenu = $tenantMenus->get($globalMenu->id);

            if (! $tenantMenu) {
                // 테넌트에 없음 → 신규
                $status = self::STATUS_NEW;
                $item = $this->buildStatusItem($globalMenu, null, $status, []);
            } else {
                // 테넌트에 있음 → 상태 판단
                $status = $this->determineStatus($tenantMenu, $globalMenu);
                $changes = $status === self::STATUS_UPDATABLE
                    ? $this->getChangedFields($tenantMenu, $globalMenu)
                    : [];
                $item = $this->buildStatusItem($globalMenu, $tenantMenu, $status, $changes);
            }

            $summary[$status]++;

            // 필터 적용
            if ($statusFilter === null || $status === $statusFilter) {
                $items[] = $item;
            }
        }

        // 2. 테넌트 메뉴 중 글로벌이 삭제된 것 확인
        foreach ($tenantMenus as $globalMenuId => $tenantMenu) {
            if (! $globalMenus->has($globalMenuId)) {
                // 글로벌 메뉴가 삭제됨
                $status = self::STATUS_DELETED;
                $item = $this->buildStatusItem(null, $tenantMenu, $status, []);

                $summary[$status]++;

                if ($statusFilter === null || $status === $statusFilter) {
                    $items[] = $item;
                }
            }
        }

        return [
            'summary' => $summary,
            'items' => $items,
        ];
    }

    /**
     * 선택 동기화 (신규 생성 또는 기존 업데이트)
     */
    public function syncMenus(array $globalMenuIds, bool $force = false): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        $synced = 0;
        $created = 0;
        $skipped = 0;
        $details = [];

        return DB::transaction(function () use ($globalMenuIds, $force, $tenantId, $userId, &$synced, &$created, &$skipped, &$details) {
            foreach ($globalMenuIds as $globalMenuId) {
                $globalMenu = Menu::global()->find($globalMenuId);

                if (! $globalMenu) {
                    $skipped++;
                    $details[] = [
                        'global_menu_id' => $globalMenuId,
                        'action' => 'skipped',
                        'reason' => 'global_not_found',
                    ];

                    continue;
                }

                // 테넌트 메뉴 확인
                $tenantMenu = Menu::withoutGlobalScopes()
                    ->where('tenant_id', $tenantId)
                    ->where('global_menu_id', $globalMenuId)
                    ->first();

                if (! $tenantMenu) {
                    // 신규 생성
                    $newMenu = $this->createFromGlobal($globalMenu, $tenantId, $userId);
                    $created++;
                    $details[] = [
                        'global_menu_id' => $globalMenuId,
                        'action' => 'created',
                        'tenant_menu_id' => $newMenu->id,
                    ];
                } else {
                    // 기존 업데이트
                    if ($tenantMenu->is_customized && ! $force) {
                        $skipped++;
                        $details[] = [
                            'global_menu_id' => $globalMenuId,
                            'action' => 'skipped',
                            'tenant_menu_id' => $tenantMenu->id,
                            'reason' => 'customized',
                        ];

                        continue;
                    }

                    $this->updateFromGlobal($tenantMenu, $globalMenu, $userId);
                    $synced++;
                    $details[] = [
                        'global_menu_id' => $globalMenuId,
                        'action' => 'updated',
                        'tenant_menu_id' => $tenantMenu->id,
                    ];
                }
            }

            return [
                'synced' => $synced,
                'created' => $created,
                'skipped' => $skipped,
                'details' => $details,
            ];
        });
    }

    /**
     * 신규 글로벌 메뉴 일괄 가져오기
     */
    public function importNewMenus(): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        // 신규 상태인 글로벌 메뉴만 가져오기
        $status = $this->getSyncStatus(self::STATUS_NEW);

        $imported = 0;
        $menus = [];

        return DB::transaction(function () use ($status, $tenantId, $userId, &$imported, &$menus) {
            foreach ($status['items'] as $item) {
                $globalMenu = Menu::global()->find($item['global_menu_id']);

                if (! $globalMenu) {
                    continue;
                }

                // 부모 메뉴 처리 (글로벌 → 테넌트 매핑)
                $newParentId = null;
                if ($globalMenu->parent_id) {
                    $parentTenantMenu = Menu::withoutGlobalScopes()
                        ->where('tenant_id', $tenantId)
                        ->where('global_menu_id', $globalMenu->parent_id)
                        ->first();

                    $newParentId = $parentTenantMenu?->id;
                }

                $newMenu = $this->createFromGlobal($globalMenu, $tenantId, $userId, $newParentId);
                $imported++;
                $menus[] = [
                    'global_menu_id' => $globalMenu->id,
                    'name' => $globalMenu->name,
                    'tenant_menu_id' => $newMenu->id,
                ];
            }

            return [
                'imported' => $imported,
                'menus' => $menus,
            ];
        });
    }

    /**
     * 변경된 기존 메뉴 일괄 업데이트 (커스텀 제외)
     */
    public function syncUpdates(): array
    {
        $tenantId = $this->tenantId();
        $userId = $this->apiUserId();

        // updatable 상태인 메뉴만 가져오기
        $status = $this->getSyncStatus(self::STATUS_UPDATABLE);

        $updated = 0;
        $skippedCustomized = 0;
        $details = [];
        $skipped = [];

        return DB::transaction(function () use ($status, $tenantId, $userId, &$updated, &$skippedCustomized, &$details, &$skipped) {
            foreach ($status['items'] as $item) {
                $tenantMenu = Menu::withoutGlobalScopes()
                    ->where('tenant_id', $tenantId)
                    ->where('id', $item['tenant_menu_id'])
                    ->first();

                if (! $tenantMenu) {
                    continue;
                }

                // 커스텀 메뉴 건너뛰기
                if ($tenantMenu->is_customized) {
                    $skippedCustomized++;
                    $skipped[] = [
                        'global_menu_id' => $item['global_menu_id'],
                        'tenant_menu_id' => $tenantMenu->id,
                        'reason' => 'customized',
                    ];

                    continue;
                }

                $globalMenu = Menu::global()->find($item['global_menu_id']);

                if (! $globalMenu) {
                    continue;
                }

                $this->updateFromGlobal($tenantMenu, $globalMenu, $userId);
                $updated++;
                $details[] = [
                    'global_menu_id' => $item['global_menu_id'],
                    'tenant_menu_id' => $tenantMenu->id,
                    'changes' => $item['changes'],
                ];
            }

            return [
                'updated' => $updated,
                'skipped_customized' => $skippedCustomized,
                'details' => $details,
                'skipped' => $skipped,
            ];
        });
    }

    /**
     * 복제 가능한 글로벌 메뉴 목록 (테넌트가 아직 복제하지 않은 것)
     */
    public function getAvailableGlobalMenus(): Collection
    {
        $tenantId = $this->tenantId();

        // 테넌트가 이미 복제한 글로벌 메뉴 ID 목록
        $clonedIds = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->whereNotNull('global_menu_id')
            ->pluck('global_menu_id')
            ->toArray();

        // 아직 복제하지 않은 글로벌 메뉴
        return Menu::global()
            ->where('is_active', true)
            ->whereNotIn('id', $clonedIds)
            ->orderByRaw('COALESCE(parent_id, 0)')
            ->orderBy('sort_order')
            ->get();
    }

    /**
     * 동기화 상태 판단
     */
    private function determineStatus(Menu $tenantMenu, Menu $globalMenu): string
    {
        // 커스터마이징된 메뉴
        if ($tenantMenu->is_customized) {
            return self::STATUS_CUSTOMIZED;
        }

        // 변경 여부 확인
        $hasChanges = $this->hasChanges($tenantMenu, $globalMenu);

        return $hasChanges ? self::STATUS_UPDATABLE : self::STATUS_UP_TO_DATE;
    }

    /**
     * 변경 여부 확인
     */
    private function hasChanges(Menu $tenantMenu, Menu $globalMenu): bool
    {
        foreach (Menu::getSyncFields() as $field) {
            if ($tenantMenu->$field !== $globalMenu->$field) {
                return true;
            }
        }

        return false;
    }

    /**
     * 변경된 필드 목록 조회
     */
    private function getChangedFields(Menu $tenantMenu, Menu $globalMenu): array
    {
        $changes = [];

        foreach (Menu::getSyncFields() as $field) {
            if ($tenantMenu->$field !== $globalMenu->$field) {
                $changes[] = $field;
            }
        }

        return $changes;
    }

    /**
     * 상태 아이템 구성
     */
    private function buildStatusItem(?Menu $globalMenu, ?Menu $tenantMenu, string $status, array $changes): array
    {
        return [
            'global_menu_id' => $globalMenu?->id,
            'global_name' => $globalMenu?->name,
            'global_url' => $globalMenu?->url,
            'global_icon' => $globalMenu?->icon,
            'status' => $status,
            'tenant_menu_id' => $tenantMenu?->id,
            'tenant_name' => $tenantMenu?->name,
            'is_customized' => $tenantMenu?->is_customized ?? false,
            'changes' => $changes,
        ];
    }

    /**
     * 글로벌 메뉴로부터 테넌트 메뉴 생성
     */
    private function createFromGlobal(Menu $globalMenu, int $tenantId, int $userId, ?int $parentId = null): Menu
    {
        return Menu::create([
            'tenant_id' => $tenantId,
            'parent_id' => $parentId,
            'global_menu_id' => $globalMenu->id,
            'name' => $globalMenu->name,
            'url' => $globalMenu->url,
            'icon' => $globalMenu->icon,
            'sort_order' => $globalMenu->sort_order,
            'is_active' => $globalMenu->is_active,
            'hidden' => $globalMenu->hidden,
            'is_customized' => false,
            'is_external' => $globalMenu->is_external,
            'external_url' => $globalMenu->external_url,
            'created_by' => $userId,
            'updated_by' => $userId,
        ]);
    }

    /**
     * 글로벌 메뉴로부터 테넌트 메뉴 업데이트
     */
    private function updateFromGlobal(Menu $tenantMenu, Menu $globalMenu, int $userId): Menu
    {
        $tenantMenu->update([
            'name' => $globalMenu->name,
            'url' => $globalMenu->url,
            'icon' => $globalMenu->icon,
            'sort_order' => $globalMenu->sort_order,
            'is_active' => $globalMenu->is_active,
            'hidden' => $globalMenu->hidden,
            'is_customized' => false, // 동기화 후 커스텀 플래그 해제
            'is_external' => $globalMenu->is_external,
            'external_url' => $globalMenu->external_url,
            'updated_by' => $userId,
        ]);

        return $tenantMenu->fresh();
    }
}
