<?php

namespace App\Http\Controllers;

use App\Models\Products\CommonCode;
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 CommonCodeSyncController 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('common-codes.sync.index'));
        }

        $environments = $this->getEnvironments();
        $selectedEnv = $request->get('env', 'dev');
        $selectedType = $request->get('type', 'global'); // global or tenant

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

        // 로컬 코드 조회 (타입 필터 적용)
        $localCodes = $this->getCodeList($selectedType);

        // 원격 코드 조회
        $remoteCodes = [];
        $remoteError = null;
        $this->remoteTenantName = null;

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

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

        return view('common-codes.sync', [
            'environments' => $environments,
            'selectedEnv' => $selectedEnv,
            'selectedType' => $selectedType,
            'localCodes' => $localCodes,
            'remoteCodes' => $remoteCodes,
            'remoteError' => $remoteError,
            'diff' => $diff,
            'localTenantName' => $localTenant?->company_name ?? '알 수 없음',
            'remoteTenantName' => $this->remoteTenantName,
        ]);
    }

    /**
     * 공통코드 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);
        }

        $type = $request->get('type', 'all'); // global, tenant, or all
        $codes = $this->getCodeList($type);

        $tenant = Tenant::find($this->getTenantId());

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

    /**
     * 공통코드 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([
            'codes' => 'required|array',
            'codes.*.tenant_id' => 'nullable|integer',
            'codes.*.code_group' => 'required|string|max:50',
            'codes.*.code' => 'required|string|max:50',
            'codes.*.name' => 'required|string|max:100',
            'codes.*.sort_order' => 'nullable|integer',
            'codes.*.attributes' => 'nullable|array',
            'codes.*.is_active' => 'nullable|boolean',
        ]);

        $imported = 0;
        $skipped = 0;

        foreach ($validated['codes'] as $codeData) {
            // 동일 코드 존재 확인
            $exists = CommonCode::query()
                ->where('tenant_id', $codeData['tenant_id'] ?? null)
                ->where('code_group', $codeData['code_group'])
                ->where('code', $codeData['code'])
                ->exists();

            if ($exists) {
                $skipped++;

                continue;
            }

            CommonCode::create([
                'tenant_id' => $codeData['tenant_id'] ?? null,
                'code_group' => $codeData['code_group'],
                'code' => $codeData['code'],
                'name' => $codeData['name'],
                'sort_order' => $codeData['sort_order'] ?? 0,
                'attributes' => $codeData['attributes'] ?? null,
                'is_active' => $codeData['is_active'] ?? true,
            ]);
            $imported++;
        }

        return response()->json([
            'success' => true,
            'message' => "{$imported}개 코드가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
            'imported' => $imported,
            'skipped' => $skipped,
        ]);
    }

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

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

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

        // 선택된 코드 조회 (타입 필터 적용)
        $localCodes = $this->getCodeList($validated['type']);
        $selectedCodes = array_filter($localCodes, function ($code) use ($validated) {
            $key = $this->makeCodeKey($code);

            return in_array($key, $validated['code_keys']);
        });

        if (empty($selectedCodes)) {
            return response()->json(['error' => '선택된 코드가 없습니다.'], 400);
        }

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

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

            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',
            'type' => 'required|string|in:global,tenant',
            'code_keys' => 'required|array|min:1',
            'code_keys.*' => 'string',
        ]);

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

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

        // 원격 코드 조회 (타입 필터 적용)
        try {
            $remoteCodes = $this->fetchRemoteCodes($env, $validated['type']);
        } catch (\Exception $e) {
            return response()->json(['error' => $e->getMessage()], 500);
        }

        // 선택된 코드만 필터링
        $selectedCodes = array_filter($remoteCodes, function ($code) use ($validated) {
            $key = $this->makeCodeKey($code);

            return in_array($key, $validated['code_keys']);
        });

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

        // 로컬에 Import
        $imported = 0;
        $skipped = 0;

        foreach ($selectedCodes as $codeData) {
            // 동일 코드 존재 확인
            $exists = CommonCode::query()
                ->where('tenant_id', $codeData['tenant_id'] ?? null)
                ->where('code_group', $codeData['code_group'])
                ->where('code', $codeData['code'])
                ->exists();

            if ($exists) {
                $skipped++;

                continue;
            }

            CommonCode::create([
                'tenant_id' => $codeData['tenant_id'] ?? null,
                'code_group' => $codeData['code_group'],
                'code' => $codeData['code'],
                'name' => $codeData['name'],
                'sort_order' => $codeData['sort_order'] ?? 0,
                'attributes' => $codeData['attributes'] ?? null,
                'is_active' => $codeData['is_active'] ?? true,
            ]);
            $imported++;
        }

        return response()->json([
            'success' => true,
            'message' => "{$imported}개 코드가 동기화되었습니다.".($skipped > 0 ? " ({$skipped}개 스킵)" : ''),
            'imported' => $imported,
            'skipped' => $skipped,
        ]);
    }

    /**
     * 코드 목록 조회
     *
     * @param  string  $type  'global', 'tenant', or 'all'
     */
    private function getCodeList(string $type = 'all'): array
    {
        $tenantId = $this->getTenantId();
        $codes = [];

        // 글로벌 코드 (tenant_id IS NULL)
        if ($type === 'global' || $type === 'all') {
            $globalCodes = CommonCode::query()
                ->whereNull('tenant_id')
                ->orderBy('code_group')
                ->orderBy('sort_order')
                ->get();

            foreach ($globalCodes as $code) {
                $codes[] = [
                    'tenant_id' => null,
                    'code_group' => $code->code_group,
                    'code' => $code->code,
                    'name' => $code->name,
                    'sort_order' => $code->sort_order,
                    'attributes' => $code->attributes,
                    'is_active' => $code->is_active,
                    'is_global' => true,
                ];
            }
        }

        // 테넌트 코드
        if ($type === 'tenant' || $type === 'all') {
            $tenantCodes = CommonCode::query()
                ->where('tenant_id', $tenantId)
                ->orderBy('code_group')
                ->orderBy('sort_order')
                ->get();

            foreach ($tenantCodes as $code) {
                $codes[] = [
                    'tenant_id' => $code->tenant_id,
                    'code_group' => $code->code_group,
                    'code' => $code->code,
                    'name' => $code->name,
                    'sort_order' => $code->sort_order,
                    'attributes' => $code->attributes,
                    'is_active' => $code->is_active,
                    'is_global' => false,
                ];
            }
        }

        return $codes;
    }

    /**
     * 원격 코드 조회
     */
    private function fetchRemoteCodes(array $env, string $type = 'all'): array
    {
        $response = Http::withHeaders([
            'X-Menu-Sync-Key' => $env['api_key'],
            'Accept' => 'application/json',
        ])->timeout(10)->get(rtrim($env['url'], '/').'/common-code-sync/export', [
            'type' => $type,
        ]);

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

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

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

        return $data['codes'];
    }

    /**
     * 코드 키 생성 (유니크 식별자)
     */
    private function makeCodeKey(array $code): string
    {
        $tenantPart = $code['tenant_id'] ?? 'global';

        return "{$tenantPart}:{$code['code_group']}:{$code['code']}";
    }

    /**
     * 차이점 계산
     */
    private function calculateDiff(array $localCodes, array $remoteCodes): array
    {
        $localKeys = array_map(fn ($c) => $this->makeCodeKey($c), $localCodes);
        $remoteKeys = array_map(fn ($c) => $this->makeCodeKey($c), $remoteCodes);

        return [
            'local_only' => array_values(array_diff($localKeys, $remoteKeys)),
            'remote_only' => array_values(array_diff($remoteKeys, $localKeys)),
            'both' => array_values(array_intersect($localKeys, $remoteKeys)),
        ];
    }
}
