import assert from 'node:assert/strict';
import { test } from 'node:test';
import { PSWG_TRANSLATIONS } from './pswg.translation.seed';
import { MINISTRY_TRANSLATIONS } from './ministry.translation.seed';
import { isSidebarTranslation } from '../../../../frontend/constants/sidebar-translation-keys';
import { i18n as ministryI18n } from '../../../../frontend/features/ministry/meeting-request/meeting-request-i18n';
import {
  ministryMeetingRequestKey,
  resolveMinistryMeetingRequestText,
  fetchMinistryMeetingRequestDictionary,
} from '../../../../frontend/features/ministry/meeting-request/meeting-request-translations';
import { seedTranslationItems } from './translation-seed.runner';
import { i18n } from '../../../../frontend/features/pswg/meeting-request/meeting-request-i18n';
import {
  meetingRequestTranslationKey,
  resolveMeetingRequestText,
  unwrapMeetingRequestDictionary,
  fetchMeetingRequestDictionary,
} from '../../../../frontend/features/pswg/meeting-request/meeting-request-translations';

const items = PSWG_TRANSLATIONS.filter(
  (item) => item.page === 'meeting_requests',
);
function flatten(value: object, prefix = ''): Record<string, string> {
  return Object.assign(
    {},
    ...Object.entries(value).map(([key, entry]) => {
      const path = prefix ? prefix + '.' + key : key;
      return typeof entry === 'string'
        ? { [path]: entry }
        : flatten(entry, path);
    }),
  );
}

test('every UI label has an active English/Khmer seed and keys are unique', () => {
  const en = flatten(i18n('en')),
    km = flatten(i18n('km'));
  assert.deepEqual(Object.keys(en).sort(), Object.keys(km).sort());
  assert.equal(
    new Set(items.map((item) => item.translationKey)).size,
    items.length,
  );
  for (const path of Object.keys(en)) {
    const item = items.find(
      (item) => item.translationKey === meetingRequestTranslationKey(path),
    );
    assert.ok(item, 'Missing seed for ' + path);
    assert.ok(item.englishText.trim());
    assert.ok(item.khmerText.trim());
    assert.equal(item.isActive, true);
  }
});

test('Admin translations reach forms, filters, status, imported detail and print', () => {
  for (const lang of ['en', 'km'] as const) {
    const dictionary = Object.fromEntries(
      Object.keys(flatten(i18n(lang))).map((path) => [
        meetingRequestTranslationKey(path),
        'Admin ' + lang + ':' + path,
      ]),
    );
    const resolved = flatten(resolveMeetingRequestText(lang, dictionary));
    for (const path of Object.keys(resolved)) {
      assert.equal(
        resolved[path],
        dictionary[meetingRequestTranslationKey(path)],
      );
    }
    assert.notEqual(resolved.title, resolved.meetingRequests);
  }
});

test('missing, blank and unknown entries keep safe fallbacks without mutating defaults', () => {
  const before = JSON.stringify(i18n('km'));
  const resolved = resolveMeetingRequestText('km', {
    title: 'Sidebar only',
    page_title: 'Admin heading',
    'form.title': 'Admin form title',
    'status.drafted': 'Admin draft',
    close: '',
    no_data: '  ',
    unknown: 'ignored',
  });
  assert.equal(resolved.meetingRequests, 'Admin heading');
  assert.equal(resolved.title, 'Admin form title');
  assert.equal(resolved.statuses.Drafted, 'Admin draft');
  assert.equal(resolved.close, i18n('km').close);
  assert.equal(resolved.noData, i18n('km').noData);
  assert.equal('unknown' in resolved, false);
  assert.equal(JSON.stringify(i18n('km')), before);
});

test('sidebar title cannot change either module page heading or form title', () => {
  assert.equal(isSidebarTranslation({ page: 'meeting_requests', translationKey: 'title' }), true);
  for (const translationKey of ['page_title', 'subtitle', 'form.title', 'view.calendar']) {
    assert.equal(isSidebarTranslation({ page: 'meeting_requests', translationKey }), false);
  }
  for (const language of ['en', 'km'] as const) {
    const sidebarOnly = { title: 'Sidebar edited' };
    assert.equal(
      resolveMeetingRequestText(language, sidebarOnly).meetingRequests,
      i18n(language).meetingRequests,
    );
    assert.equal(
      resolveMinistryMeetingRequestText(language, sidebarOnly).title,
      ministryI18n[language].title,
    );
    const labels = {
      ...sidebarOnly,
      page_title: 'Page edited',
      'form.title': 'Form edited',
    };
    assert.equal(
      resolveMeetingRequestText(language, labels).meetingRequests,
      'Page edited',
    );
    assert.equal(
      resolveMeetingRequestText(language, labels).title,
      'Form edited',
    );
    assert.equal(
      resolveMinistryMeetingRequestText(language, labels).title,
      'Page edited',
    );
  }
});

test('Ministry has its own complete bilingual seed for shared UI labels', () => {
  const seeds = MINISTRY_TRANSLATIONS.filter(
    (item) => item.page === 'meeting_requests',
  );
  assert.equal(
    new Set(seeds.map((item) => item.translationKey)).size,
    seeds.length,
  );
  const en = flatten(ministryI18n.en),
    km = flatten(ministryI18n.km);
  assert.deepEqual(Object.keys(en).sort(), Object.keys(km).sort());
  for (const path of Object.keys(en)) {
    const seed = seeds.find(
      (item) => item.translationKey === ministryMeetingRequestKey(path),
    );
    assert.ok(seed, path);
    assert.equal(seed.englishText, en[path]);
    assert.equal(seed.khmerText, km[path]);
  }
});

test('same shared UI keys remain isolated between PSWG and Ministry APIs', async () => {
  const original = globalThis.fetch;
  try {
    globalThis.fetch = async (input) => {
      const url = new URL(String(input));
      assert.equal(url.searchParams.get('page'), 'meeting_requests');
      const module = url.searchParams.get('module');
      assert.ok(module === 'pswg' || module === 'ministry');
      const prefix = module + ':' + url.searchParams.get('language');
      return Response.json({
        data: {
          translations: {
            page_title: prefix + ':heading',
            'meeting_calendar.detail.close': prefix + ':close',
            'print_labels.matrix_heading': prefix + ':print',
          },
        },
      });
    };
    for (const language of ['en', 'km'] as const) {
      const pswg = resolveMeetingRequestText(
        language,
        await fetchMeetingRequestDictionary(language),
      );
      const ministry = resolveMinistryMeetingRequestText(
        language,
        await fetchMinistryMeetingRequestDictionary(language),
      );
      assert.equal(pswg.meetingRequests, 'pswg:' + language + ':heading');
      assert.equal(ministry.title, 'ministry:' + language + ':heading');
      assert.equal(
        pswg.meetingCalendar.detail.close,
        'pswg:' + language + ':close',
      );
      assert.equal(
        ministry.meetingCalendar.detail.close,
        'ministry:' + language + ':close',
      );
      assert.equal(
        pswg.printLabels.matrixHeading,
        'pswg:' + language + ':print',
      );
      assert.equal(
        ministry.printLabels.matrixHeading,
        'ministry:' + language + ':print',
      );
    }
  } finally {
    globalThis.fetch = original;
  }
});

test('dictionary parser handles API envelopes and rejects invalid entries', () => {
  assert.deepEqual(
    unwrapMeetingRequestDictionary({
      data: {
        data: {
          translations: {
            title: 'Admin',
            empty: '',
            whitespace: ' ',
            invalid: 5,
          },
        },
      },
    }),
    { title: 'Admin' },
  );
  assert.deepEqual(unwrapMeetingRequestDictionary(null), {});
});

test('dictionary fetch uses PSWG page, language, cancellation and no cache', async () => {
  const original = globalThis.fetch;
  try {
    for (const language of ['en', 'km'] as const) {
      const controller = new AbortController();
      globalThis.fetch = async (input, init) => {
        const url = new URL(String(input));
        assert.equal(url.searchParams.get('module'), 'pswg');
        assert.equal(url.searchParams.get('page'), 'meeting_requests');
        assert.equal(url.searchParams.get('language'), language);
        assert.equal(init?.cache, 'no-store');
        assert.equal(init?.signal, controller.signal);
        return Response.json({
          data: { translations: { title: 'Admin ' + language } },
        });
      };
      assert.deepEqual(
        await fetchMeetingRequestDictionary(language, controller.signal),
        { title: 'Admin ' + language },
      );
    }
    globalThis.fetch = async () => new Response('', { status: 503 });
    await assert.rejects(fetchMeetingRequestDictionary('en'));
  } finally {
    globalThis.fetch = original;
  }
});

test('scoped seed is repeatable and never overwrites Admin values', async () => {
  const rows = new Map<string, { id: number; englishText: string }>();
  rows.set('title', { id: 1, englishText: 'Admin custom title' });
  const database = {
    translation: {
      findUnique: async ({
        where,
      }: {
        where: { module_page_translationKey: { translationKey: string } };
      }) => rows.get(where.module_page_translationKey.translationKey) ?? null,
      create: async ({
        data,
      }: {
        data: { translationKey: string; englishText: string };
      }) => {
        const row = { id: rows.size + 1, englishText: data.englishText };
        rows.set(data.translationKey, row);
        return row;
      },
    },
  };
  const first = await seedTranslationItems(database, 'pswg', items);
  const second = await seedTranslationItems(database, 'pswg', items);
  assert.equal(first.created, items.length - 1);
  assert.equal(second.created, 0);
  assert.equal(second.kept, items.length);
  assert.equal(rows.get('title')?.englishText, 'Admin custom title');
});
