<?php

namespace App\Http\Controllers;

use App\Models\Commons\Menu;
use App\Models\Tenants\Tenant;
use App\Models\Tenants\TenantSetting;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Http;

class MenuSyncController extends Controller
{
    private ?string $remoteTenantName = null;

    /**
     * 현재 선택된 테넌트 ID
     */
    protected function getTenantId(): int
    {
        $tenantId = session('selected_tenant_id');

        return ($tenantId && $tenantId !== 'all') ? (int) $tenantId : 1;
    }

    /**
     * 환경 설정 조회
     */
    private function getEnvironments(): array
    {
        $setting = TenantSetting::withoutGlobalScopes()
            ->where('tenant_id', $this->getTenantId())
            ->where('setting_group', 'menu_sync')
            ->where('setting_key', 'environments')
            ->first();

        return $setting?->setting_value ?? [
            'dev' => ['name' => '개발', 'url' => '', 'api_key' => ''],
            'prod' => ['name' => '운영', 'url' => '', 'api_key' => ''],
        ];
    }

    /**
     * 메뉴 동기화 페이지
     */
    public function index(Request $request): View|Response
    {
        if ($request->header('HX-Request')) {
            return response('', 200)->header('HX-Redirect', route('menus.sync.index'));
        }

        $environments = $this->getEnvironments();
        $selectedEnv = $request->get('env', 'dev');

        // 로컬 테넌트 정보
        $localTenant = Tenant::find($this->getTenantId());

        // 로컬 메뉴 조회 (트리 구조)
        $localMenus = $this->getMenuTree();

        // 원격 메뉴 조회
        $remoteMenus = [];
        $remoteError = null;
        $this->remoteTenantName = null;

        if (! empty($environments[$selectedEnv]['url'])) {
            try {
                $remoteMenus = $this->fetchRemoteMenus($environments[$selectedEnv]);
            } catch (\Exception $e) {
                $remoteError = $e->getMessage();
            }
        }

        // 차이점 계산
        $diff = $this->calculateDiff($localMenus, $remoteMenus);

        return view('menus.sync', [
            'environments' => $environments,
            'selectedEnv' => $selectedEnv,
            'localMenus' => $localMenus,
            'remoteMenus' => $remoteMenus,
            'remoteError' => $remoteError,
            'diff' => $diff,
            'localTenantName' => $localTenant?->company_name ?? '알 수 없음',
            'remoteTenantName' => $this->remoteTenantName,
        ]);
    }

    /**
     * 환경 설정 저장
     */
    public function saveSettings(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'environments' => 'required|array',
            'environments.*.name' => 'required|string|max:50',
            'environments.*.url' => 'nullable|url|max:255',
            'environments.*.api_key' => 'nullable|string|max:255',
        ]);

        TenantSetting::withoutGlobalScopes()->updateOrCreate(
            [
                'tenant_id' => $this->getTenantId(),
                'setting_group' => 'menu_sync',
                'setting_key' => 'environments',
            ],
            [
                'setting_value' => $validated['environments'],
                'description' => '메뉴 동기화 환경 설정',
            ]
        );

        return response()->json(['success' => true, 'message' => '환경 설정이 저장되었습니다.']);
    }

    /**
     * 메뉴 Export API (다른 환경에서 호출)
     */
    public function export(Request $request): JsonResponse
    {
        // API Key 검증
        $apiKey = $request->header('X-Menu-Sync-Key');
        $validKey = config('app.menu_sync_api_key');

        if (empty($validKey) || $apiKey !== $validKey) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        // 요청에서 tenant_id를 받으면 사용, 없으면 세션 기반
        $tenantId = $request->query('tenant_id') ? (int) $request->query('tenant_id') : $this->getTenantId();

        $menus = $this->getMenuTreeForTenant($tenantId);
        $tenant = Tenant::find($tenantId);

        return response()->json([
            'success' => true,
            'environment' => config('app.env'),
            'tenant_name' => $tenant?->company_name ?? '알 수 없음',
            'exported_at' => now()->toIso8601String(),
            'menus' => $menus,
        ]);
    }

    /**
     * 메뉴 Import API (다른 환경에서 호출)
     */
    public function import(Request $request): JsonResponse
    {
        // API Key 검증
        $apiKey = $request->header('X-Menu-Sync-Key');
        $validKey = config('app.menu_sync_api_key');

        if (empty($validKey) || $apiKey !== $validKey) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        $validated = $request->validate([
            'menus' => 'required|array',
            'menus.*.name' => 'required|string|max:100',
            'menus.*.url' => 'required|string|max:255',
            'menus.*.icon' => 'nullable|string|max:50',
            'menus.*.sort_order' => 'nullable|integer',
            'menus.*.options' => 'nullable|array',
            'menus.*.parent_name' => 'nullable|string', // 부모 메뉴 이름으로 연결
            'menus.*.children' => 'nullable|array',
        ]);

        $imported = 0;
        foreach ($validated['menus'] as $menuData) {
            $this->importMenu($menuData);
            $imported++;
        }

        return response()->json([
            'success' => true,
            'message' => "{$imported}개 메뉴가 동기화되었습니다.",
            'imported' => $imported,
        ]);
    }

    /**
     * 개별 메뉴 Push (로컬 → 원격)
     */
    public function push(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'env' => 'required|string|in:dev,prod',
            'menu_ids' => 'required|array|min:1',
            'menu_ids.*' => 'integer',
        ]);

        $environments = $this->getEnvironments();
        $env = $environments[$validated['env']] ?? null;

        if (! $env || empty($env['url'])) {
            return response()->json(['error' => '환경 설정이 없습니다.'], 400);
        }

        // 선택된 메뉴 조회
        $menus = Menu::withoutGlobalScopes()
            ->where('tenant_id', $this->getTenantId())
            ->whereIn('id', $validated['menu_ids'])
            ->get();

        if ($menus->isEmpty()) {
            return response()->json(['error' => '선택된 메뉴가 없습니다.'], 400);
        }

        // 메뉴 데이터 준비 (부모 정보 포함)
        $menuData = $menus->map(function ($menu) {
            $parent = $menu->parent_id
                ? Menu::withoutGlobalScopes()->find($menu->parent_id)
                : null;

            return [
                'name' => $menu->name,
                'url' => $menu->url,
                'icon' => $menu->icon,
                'sort_order' => $menu->sort_order,
                'options' => $menu->options,
                'parent_name' => $parent?->name,
                'children' => $this->getChildrenData($menu->id),
            ];
        })->toArray();

        // 원격 서버로 전송
        try {
            $response = Http::withHeaders([
                'X-Menu-Sync-Key' => $env['api_key'],
                'Accept' => 'application/json',
            ])->post(rtrim($env['url'], '/').'/menu-sync/import', [
                'menus' => $menuData,
            ]);

            if ($response->successful()) {
                return response()->json([
                    'success' => true,
                    'message' => $response->json('message', '동기화 완료'),
                ]);
            }

            return response()->json([
                'error' => $response->json('error', '원격 서버 오류'),
            ], $response->status());
        } catch (\Exception $e) {
            return response()->json(['error' => '연결 실패: '.$e->getMessage()], 500);
        }
    }

    /**
     * 개별 메뉴 Pull (원격 → 로컬)
     */
    public function pull(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'env' => 'required|string|in:dev,prod',
            'menu_names' => 'required|array|min:1',
            'menu_names.*' => 'string',
        ]);

        $environments = $this->getEnvironments();
        $env = $environments[$validated['env']] ?? null;

        if (! $env || empty($env['url'])) {
            return response()->json(['error' => '환경 설정이 없습니다.'], 400);
        }

        // 원격 메뉴 조회
        try {
            $remoteMenus = $this->fetchRemoteMenus($env);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }

        // 선택된 메뉴만 필터링
        $selectedMenus = $this->filterMenusByName($remoteMenus, $validated['menu_names']);

        if (empty($selectedMenus)) {
            return response()->json(['error' => '선택된 메뉴를 찾을 수 없습니다.'], 400);
        }

        // 로컬에 Import
        $imported = 0;
        foreach ($selectedMenus as $menuData) {
            $this->importMenu($menuData);
            $imported++;
        }

        return response()->json([
            'success' => true,
            'message' => "{$imported}개 메뉴가 동기화되었습니다.",
        ]);
    }

    /**
     * 연결 테스트
     */
    public function testConnection(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'url' => 'required|url',
            'api_key' => 'required|string',
        ]);

        try {
            $response = Http::withHeaders([
                'X-Menu-Sync-Key' => $validated['api_key'],
                'Accept' => 'application/json',
            ])->timeout(10)->get(rtrim($validated['url'], '/').'/menu-sync/export');

            if ($response->successful()) {
                $data = $response->json();

                return response()->json([
                    'success' => true,
                    'message' => '연결 성공',
                    'environment' => $data['environment'] ?? 'unknown',
                    'menu_count' => count($data['menus'] ?? []),
                ]);
            }

            return response()->json([
                'success' => false,
                'message' => 'API 오류: '.$response->status(),
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'success' => false,
                'message' => '연결 실패: '.$e->getMessage(),
            ]);
        }
    }

    /**
     * 메뉴 트리 조회
     */
    private function getMenuTree(?int $parentId = null): array
    {
        return $this->getMenuTreeForTenant($this->getTenantId(), $parentId);
    }

    /**
     * 특정 테넌트의 메뉴 트리 조회
     */
    private function getMenuTreeForTenant(int $tenantId, ?int $parentId = null): array
    {
        $menus = Menu::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('parent_id', $parentId)
            ->orderBy('sort_order')
            ->get();

        return $menus->map(function ($menu) use ($tenantId) {
            return [
                'id' => $menu->id,
                'name' => $menu->name,
                'url' => $menu->url,
                'icon' => $menu->icon,
                'sort_order' => $menu->sort_order,
                'options' => $menu->options,
                'children' => $this->getMenuTreeForTenant($tenantId, $menu->id),
            ];
        })->toArray();
    }

    /**
     * 자식 메뉴 데이터 조회
     */
    private function getChildrenData(int $parentId): array
    {
        $children = Menu::withoutGlobalScopes()
            ->where('tenant_id', $this->getTenantId())
            ->where('parent_id', $parentId)
            ->orderBy('sort_order')
            ->get();

        return $children->map(function ($menu) {
            return [
                'name' => $menu->name,
                'url' => $menu->url,
                'icon' => $menu->icon,
                'sort_order' => $menu->sort_order,
                'options' => $menu->options,
                'children' => $this->getChildrenData($menu->id),
            ];
        })->toArray();
    }

    /**
     * 원격 메뉴 조회
     */
    private function fetchRemoteMenus(array $env): array
    {
        $response = Http::withHeaders([
            'X-Menu-Sync-Key' => $env['api_key'],
            'Accept' => 'application/json',
        ])->timeout(10)->get(rtrim($env['url'], '/').'/menu-sync/export', [
            'tenant_id' => $this->getTenantId(),  // 현재 선택된 테넌트 전달
        ]);

        if (! $response->successful()) {
            throw new \Exception('API 오류: HTTP '.$response->status());
        }

        $data = $response->json();
        if (! isset($data['menus'])) {
            throw new \Exception('잘못된 응답 형식');
        }

        $this->remoteTenantName = $data['tenant_name'] ?? null;

        return $data['menus'];
    }

    /**
     * 메뉴 차이점 계산
     */
    private function calculateDiff(array $localMenus, array $remoteMenus): array
    {
        $localNames = $this->flattenMenuNames($localMenus);
        $remoteNames = $this->flattenMenuNames($remoteMenus);

        return [
            'local_only' => array_diff($localNames, $remoteNames),
            'remote_only' => array_diff($remoteNames, $localNames),
            'both' => array_intersect($localNames, $remoteNames),
        ];
    }

    /**
     * 메뉴 이름 평탄화
     */
    private function flattenMenuNames(array $menus, string $prefix = ''): array
    {
        $names = [];
        foreach ($menus as $menu) {
            $fullName = $prefix ? "{$prefix} > {$menu['name']}" : $menu['name'];
            $names[] = $menu['name'];
            if (! empty($menu['children'])) {
                $names = array_merge($names, $this->flattenMenuNames($menu['children'], $fullName));
            }
        }

        return $names;
    }

    /**
     * 이름으로 메뉴 필터링 (부모 이름 포함)
     */
    private function filterMenusByName(array $menus, array $names, ?string $parentName = null): array
    {
        $result = [];
        foreach ($menus as $menu) {
            $originalChildren = $menu['children'] ?? [];

            if (in_array($menu['name'], $names)) {
                $menu['parent_name'] = $parentName;
                // children은 flat list에서 개별 처리되므로 제거 (동명 메뉴 중복 import 방지)
                $menu['children'] = [];
                $result[] = $menu;
            }

            if (! empty($originalChildren)) {
                $result = array_merge($result, $this->filterMenusByName($originalChildren, $names, $menu['name']));
            }
        }

        return $result;
    }

    /**
     * 메뉴 Import
     */
    private function importMenu(array $data, ?int $parentId = null): void
    {
        // 부모 메뉴 찾기
        if (! $parentId && ! empty($data['parent_name'])) {
            $parent = Menu::withoutGlobalScopes()
                ->where('tenant_id', $this->getTenantId())
                ->where('name', $data['parent_name'])
                ->first();
            $parentId = $parent?->id;
        }

        // name + parent_id로 매칭 (동명 메뉴가 다른 계층에 있을 때 자기참조 방지)
        $menu = Menu::withoutGlobalScopes()->updateOrCreate(
            [
                'tenant_id' => $this->getTenantId(),
                'name' => $data['name'],
                'parent_id' => $parentId,
            ],
            [
                'url' => $data['url'],
                'icon' => $data['icon'] ?? null,
                'sort_order' => $data['sort_order'] ?? 0,
                'options' => $data['options'] ?? null,
                'is_active' => true,
            ]
        );

        // 자식 메뉴 처리
        if (! empty($data['children'])) {
            foreach ($data['children'] as $child) {
                $this->importMenu($child, $menu->id);
            }
        }
    }
}
