import test from 'node:test';
import assert from 'node:assert/strict';
import {
  createDefaultChatTypeExecutionPolicy,
  migrateLegacyChatTypeContexts,
  sanitizePersistedChatTypes,
  synchronizeBuiltinCodexChatTypes,
  resolveAppConfigByOrigin,
  resolveCanonicalChatTypesFromConfig,
  resolveCanonicalChatContextSettingsFromConfig,
  stripChatContextSettingsFromScopedAppConfigs,
  stripSharedContextDataFromScopedAppConfigs,
} from './app-config-service.js';

test('sanitizePersistedChatTypes keeps saved chat type edits as-is', () => {
  const merged = sanitizePersistedChatTypes([
    {
      id: 'general-request',
      name: '일반 요청',
      sortOrder: 3,
      description: '사용자가 수정한 일반 요청 문맥',
      permissions: ['guest', 'token-user'],
      enabled: true,
      updatedAt: '2026-04-24T09:00:00.000Z',
    },
  ]);

  const generalRequest = merged.find((item) => item.id === 'general-request');

  assert.ok(generalRequest);
  assert.equal(generalRequest.description, '사용자가 수정한 일반 요청 문맥');
  assert.deepEqual(generalRequest.permissions, ['guest', 'token-user']);
  assert.equal(generalRequest.sortOrder, 1);
});

test('sanitizePersistedChatTypes keeps saved layout editor execution entries', () => {
  const merged = sanitizePersistedChatTypes([
    {
      id: 'layout-editor-execution',
      name: 'Layout editor 실행',
      sortOrder: 2,
      description: '호출 가능한 API 요청만 처리합니다.',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-04-27T00:00:00.000Z',
    },
  ]);

  const layoutEditorExecution = merged.find((item) => item.id === 'layout-editor-execution');

  assert.ok(layoutEditorExecution);
  assert.equal(layoutEditorExecution.description, '호출 가능한 API 요청만 처리합니다.');
  assert.equal(layoutEditorExecution.sortOrder, 1);
});

test('sanitizePersistedChatTypes keeps saved guided layout editor entries', () => {
  const merged = sanitizePersistedChatTypes([
    {
      id: 'layout-editor-guided-execution',
      name: 'Layout editor 단계별 실행',
      sortOrder: 4,
      description: '사용자가 정리한 단계별 Layout 실행 문맥',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-01T09:00:00.000Z',
    },
  ]);

  const guidedLayoutEditorExecution = merged.find((item) => item.id === 'layout-editor-guided-execution');

  assert.ok(guidedLayoutEditorExecution);
  assert.equal(guidedLayoutEditorExecution.description, '사용자가 정리한 단계별 Layout 실행 문맥');
  assert.equal(guidedLayoutEditorExecution.sortOrder, 1);
});

test('sanitizePersistedChatTypes returns empty list when nothing is saved', () => {
  const merged = sanitizePersistedChatTypes([]);

  assert.deepEqual(merged, []);
});

test('sanitizePersistedChatTypes keeps saved chat type list without backfilling removed entries', () => {
  const merged = sanitizePersistedChatTypes([
    {
      id: 'general-request',
      name: '일반 요청',
      sortOrder: 2,
      description: '사용자가 수정한 일반 요청 문맥',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
    {
      id: 'custom-support-flow',
      name: '운영 문의 전용',
      sortOrder: 1,
      description: 'custom',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
  ]);

  assert.ok(!merged.some((item) => item.id === 'layout-editor-guided-execution'));
  assert.ok(!merged.some((item) => item.id === 'layout-editor-execution'));
  assert.ok(merged.some((item) => item.id === 'custom-support-flow'));
});

test('sanitizePersistedChatTypes keeps all saved chat types without special filtering', () => {
  const stripped = sanitizePersistedChatTypes([
    {
      id: 'general-request',
      name: '일반 요청',
      sortOrder: 2,
      description: 'builtin',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
    {
      id: 'plan-checklist-execution',
      name: 'Plan 체크리스트 실행',
      sortOrder: 3,
      description: 'custom-seeded',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
    {
      id: 'custom-support-flow',
      name: '운영 문의 전용',
      sortOrder: 1,
      description: 'custom',
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
  ]);

  assert.deepEqual(stripped.map((item) => item.id), ['custom-support-flow', 'general-request', 'plan-checklist-execution']);
});

test('synchronizeBuiltinCodexChatTypes upgrades legacy codex summary execution policy', () => {
  const synced = synchronizeBuiltinCodexChatTypes([
    {
      id: 'codex-summary',
      name: 'Codex 종합',
      sortOrder: 13,
      description:
        '## 처리 범위\n- Codex 봇들의 대화와 의견 수렴 과정을 거친 뒤 최종 결과물을 정리해 제공하는 채팅에 사용합니다.\n- Plan 자동화 문맥을 기본값으로 섞지 않고, 현재 채팅방 공통 문맥과 사용자 요청만 기준으로 결과를 정리합니다.\n\n## 응답 방식\n- 중간 대화에서 나온 핵심 의견, 판단 근거, 남은 쟁점을 압축해 정리합니다.\n- 최종 답변에는 필요한 산출물, 검증 결과, 적용 결론을 함께 제공합니다.',
      executionPolicy: createDefaultChatTypeExecutionPolicy(),
      permissions: ['token-user'],
      enabled: true,
      updatedAt: '2026-05-17T00:00:00.000Z',
    },
  ]);

  const codexSummary = synced.find((item) => item.id === 'codex-summary');
  const codexDispatcher = synced.find((item) => item.id === 'codex-dispatcher-workers');
  const codexLiveDefault = synced.find((item) => item.id === 'codex-live-default');

  assert.ok(codexSummary);
  assert.equal(codexSummary.executionPolicy.mode, 'summary-free-talking');
  assert.match(codexSummary.description, /회의 기록자 1명/);
  assert.ok(codexDispatcher);
  assert.equal(codexDispatcher.executionPolicy.mode, 'dispatcher-workers');
  assert.ok(codexLiveDefault);
  assert.equal(codexLiveDefault.executionPolicy.mode, 'default');
});

test('migrateLegacyChatTypeContexts moves legacy plan checklist chat type into default context settings', () => {
  const migrated = migrateLegacyChatTypeContexts(
    {
      defaultContexts: [],
      chatTypeDefaults: [
        {
          chatTypeId: 'plan-checklist-execution',
          defaultContextIds: ['legacy-linked-context'],
          updatedAt: '2026-05-08T00:00:00.000Z',
        },
      ],
      roomContexts: [],
    },
    [
      {
        id: 'plan-checklist-execution',
        name: 'Plan 체크리스트 실행',
        sortOrder: 1,
        description: 'legacy plan context',
        executionPolicy: createDefaultChatTypeExecutionPolicy(),
        permissions: ['token-user'],
        enabled: true,
        updatedAt: '2026-05-08T00:00:00.000Z',
      },
    ],
  );

  assert.equal(migrated.defaultContexts.some((item) => item.id === 'chat-default-plan-checklist-execution'), true);
  assert.equal(
    migrated.defaultContexts.find((item) => item.id === 'chat-default-plan-checklist-execution')?.content,
    'legacy plan context',
  );
  assert.equal(migrated.chatTypeDefaults.some((item) => item.chatTypeId === 'plan-checklist-execution'), false);
});

test('resolveAppConfigByOrigin prefers scoped app config over legacy global config', () => {
  const resolved = resolveAppConfigByOrigin(
    {
      chat: {
        maxContextMessages: 12,
        receiveRoomNotifications: true,
      },
      automation: {
        notifyOnAutomationStart: true,
      },
      scopedAppConfigs: {
        'https://rel.sm-home.cloud': {
          config: {
            chat: {
              receiveRoomNotifications: false,
            },
          },
        },
      },
    },
    'https://rel.sm-home.cloud',
  ) as {
    chat?: { maxContextMessages?: number; receiveRoomNotifications?: boolean };
    automation?: { notifyOnAutomationStart?: boolean };
  };

  assert.equal(resolved.chat?.maxContextMessages, 12);
  assert.equal(resolved.chat?.receiveRoomNotifications, false);
  assert.equal(resolved.automation?.notifyOnAutomationStart, true);
});

test('resolveAppConfigByOrigin falls back to legacy global config when scoped config is missing', () => {
  const resolved = resolveAppConfigByOrigin(
    {
      chat: {
        receiveRoomNotifications: true,
      },
    },
    'https://preview.sm-home.cloud',
  ) as {
    chat?: { receiveRoomNotifications?: boolean };
  };

  assert.equal(resolved.chat?.receiveRoomNotifications, true);
});

test('resolveCanonicalChatContextSettingsFromConfig prefers global chat context settings over stale scoped entries', () => {
  const resolved = resolveCanonicalChatContextSettingsFromConfig(
    {
      chatContextSettings: {
        defaultContexts: [
          {
            id: 'global-a',
            title: '전역 A',
            content: 'global',
            enabled: true,
            updatedAt: '2026-05-08T00:00:00.000Z',
          },
          {
            id: 'global-b',
            title: '전역 B',
            content: 'global',
            enabled: true,
            updatedAt: '2026-05-08T00:00:00.000Z',
          },
        ],
      },
      scopedAppConfigs: {
        'https://preview.sm-home.cloud': {
          config: {
            chatContextSettings: {
              defaultContexts: [
                {
                  id: 'scoped-a',
                  title: '스코프 A',
                  content: 'scoped',
                  enabled: true,
                  updatedAt: '2026-05-01T00:00:00.000Z',
                },
              ],
            },
          },
        },
      },
    },
    'https://preview.sm-home.cloud',
  );

  assert.deepEqual(
    resolved.defaultContexts.map((item) => item.id),
    ['global-a', 'global-b'],
  );
});

test('resolveCanonicalChatContextSettingsFromConfig keeps saved default context sort order and renumbers gaps', () => {
  const resolved = resolveCanonicalChatContextSettingsFromConfig({
    chatContextSettings: {
      defaultContexts: [
        {
          id: 'context-b',
          title: 'B 문맥',
          sortOrder: 3,
          content: 'b',
          enabled: true,
          updatedAt: '2026-05-08T00:00:00.000Z',
        },
        {
          id: 'context-a',
          title: 'A 문맥',
          sortOrder: 1,
          content: 'a',
          enabled: true,
          updatedAt: '2026-05-08T00:00:00.000Z',
        },
      ],
    },
  });

  assert.deepEqual(
    resolved.defaultContexts.map((item) => ({ id: item.id, sortOrder: item.sortOrder })),
    [
      { id: 'context-a', sortOrder: 1 },
      { id: 'context-b', sortOrder: 2 },
    ],
  );
});

test('resolveCanonicalChatContextSettingsFromConfig appends unsorted default contexts after sorted entries', () => {
  const resolved = resolveCanonicalChatContextSettingsFromConfig({
    chatContextSettings: {
      defaultContexts: [
        {
          id: 'context-b',
          title: 'B 문맥',
          content: 'b',
          enabled: true,
          updatedAt: '2026-05-08T00:00:00.000Z',
        },
        {
          id: 'context-a',
          title: 'A 문맥',
          sortOrder: 1,
          content: 'a',
          enabled: true,
          updatedAt: '2026-05-08T00:00:00.000Z',
        },
      ],
    },
  });

  assert.deepEqual(
    resolved.defaultContexts.map((item) => ({ id: item.id, sortOrder: item.sortOrder })),
    [
      { id: 'context-a', sortOrder: 1 },
      { id: 'context-b', sortOrder: 2 },
    ],
  );
});

test('resolveCanonicalChatContextSettingsFromConfig falls back to scoped settings only when global settings are empty', () => {
  const resolved = resolveCanonicalChatContextSettingsFromConfig(
    {
      scopedAppConfigs: {
        'https://preview.sm-home.cloud': {
          config: {
            chatContextSettings: {
              defaultContexts: [
                {
                  id: 'scoped-a',
                  title: '스코프 A',
                  content: 'scoped',
                  enabled: true,
                  updatedAt: '2026-05-01T00:00:00.000Z',
                },
              ],
            },
          },
        },
      },
    },
    'https://preview.sm-home.cloud',
  );

  assert.deepEqual(
    resolved.defaultContexts.map((item) => item.id),
    ['scoped-a'],
  );
});

test('resolveCanonicalChatTypesFromConfig merges global chat types with stale scoped entries', () => {
  const resolved = resolveCanonicalChatTypesFromConfig(
    {
      chatTypes: [
        {
          id: 'verification-test-generation',
          name: '검증 밑 테스트 생성',
          description: 'global',
          permissions: ['token-user'],
          enabled: true,
          updatedAt: '2026-05-08T08:15:18.440Z',
        },
      ],
      scopedAppConfigs: {
        'https://preview.sm-home.cloud': {
          config: {
            chatTypes: [
              {
                id: 'general-request',
                name: '일반 요청',
                description: 'scoped',
                permissions: ['token-user'],
                enabled: true,
                updatedAt: '2026-05-01T00:00:00.000Z',
              },
            ],
          },
        },
      },
    },
    'https://preview.sm-home.cloud',
  );

  assert.ok(resolved);
  assert.equal(resolved.some((item) => item.id === 'verification-test-generation'), true);
  assert.equal(resolved.some((item) => item.id === 'general-request'), true);
});

test('stripChatContextSettingsFromScopedAppConfigs removes stale scoped chat context settings only', () => {
  const stripped = stripChatContextSettingsFromScopedAppConfigs({
    scopedAppConfigs: {
      'https://preview.sm-home.cloud': {
        config: {
          chatContextSettings: {
            defaultContexts: [{ id: 'legacy', title: 'legacy', content: 'legacy', enabled: true }],
          },
          chat: {
            receiveRoomNotifications: false,
          },
        },
        updatedAt: '2026-05-08T00:00:00.000Z',
      },
    },
  });

  assert.equal(stripped.changed, true);
  assert.deepEqual(stripped.scopedConfigs, {
    'https://preview.sm-home.cloud': {
      config: {
        chat: {
          receiveRoomNotifications: false,
        },
      },
      updatedAt: '2026-05-08T00:00:00.000Z',
    },
  });
});

test('stripSharedContextDataFromScopedAppConfigs removes scoped chat-type/context data and backs up non-shared origins', () => {
  const stripped = stripSharedContextDataFromScopedAppConfigs(
    {
      scopedAppConfigs: {
        'https://preview.sm-home.cloud': {
          config: {
            chatTypes: [
              {
                id: 'general-request',
                name: '일반 요청',
                description: 'preview-shared',
                permissions: ['token-user'],
                enabled: true,
                updatedAt: '2026-05-08T00:00:00.000Z',
              },
            ],
            chatContextSettings: {
              defaultContexts: [
                {
                  id: 'preview-context',
                  title: 'preview',
                  content: 'shared',
                  enabled: true,
                  updatedAt: '2026-05-08T00:00:00.000Z',
                },
              ],
            },
            chat: {
              receiveRoomNotifications: false,
            },
          },
          updatedAt: '2026-05-08T00:00:00.000Z',
          appDomain: 'preview.sm-home.cloud',
        },
        'https://test.sm-home.cloud': {
          config: {
            chatTypes: [
              {
                id: 'chat-type-test-temp',
                name: '임시 유형',
                description: 'test-only',
                permissions: ['token-user'],
                enabled: true,
                updatedAt: '2026-05-08T00:00:00.000Z',
              },
            ],
            chatContextSettings: {
              defaultContexts: [
                {
                  id: 'test-context',
                  title: 'test',
                  content: 'legacy',
                  enabled: true,
                  updatedAt: '2026-05-08T00:00:00.000Z',
                },
              ],
            },
            automation: {
              notifyOnAutomationStart: true,
            },
          },
          updatedAt: '2026-05-08T00:00:00.000Z',
          appDomain: 'test.sm-home.cloud',
        },
      },
    },
    'https://preview.sm-home.cloud',
  );

  assert.equal(stripped.changed, true);
  assert.deepEqual(stripped.scopedConfigs, {
    'https://preview.sm-home.cloud': {
      config: {
        chat: {
          receiveRoomNotifications: false,
        },
      },
      updatedAt: '2026-05-08T00:00:00.000Z',
      appDomain: 'preview.sm-home.cloud',
    },
    'https://test.sm-home.cloud': {
      config: {
        automation: {
          notifyOnAutomationStart: true,
        },
      },
      updatedAt: '2026-05-08T00:00:00.000Z',
      appDomain: 'test.sm-home.cloud',
    },
  });
  assert.equal(
    Array.isArray(stripped.backups['https://test.sm-home.cloud']?.chatTypes),
    true,
  );
  assert.equal(
    stripped.backups['https://test.sm-home.cloud']?.chatContextSettings?.defaultContexts[0]?.id,
    'test-context',
  );
  assert.equal(stripped.backups['https://preview.sm-home.cloud'], undefined);
});
