import { expect, type APIRequestContext, type Page, test } from '@playwright/test';

const BACKEND_API_URL = process.env.VITE_BACKEND_API_URL || 'http://localhost:8080/api';

const TEST_USER = {
  email: 'admin@flatlogic.com',
  password: 'flatlogicAdmin123!',
};

async function authenticateViaPage(page: Page): Promise<void> {
  await page.goto('/');
  await page.getByPlaceholder('you@school.edu').fill(TEST_USER.email);
  await page.getByPlaceholder('Enter your password').fill(TEST_USER.password);
  await page.getByRole('button', { name: 'Sign In', exact: true }).click();
  await page.waitForURL('/', { timeout: 10000 });
}

interface ClassroomStrategyPayload {
  readonly title: string;
}

interface SignLanguagePayload {
  readonly word: string;
}

interface ZonePayload {
  readonly name: string;
}

interface TimerSoundPayload {
  readonly name: string;
}

interface SeededContentTypeExpectation {
  readonly contentType: string;
  readonly payloadShape: 'array' | 'object';
}

const MINIMUM_SEEDED_CONTENT_TYPES: readonly SeededContentTypeExpectation[] = [
  { contentType: 'classroom-timer-backgrounds', payloadShape: 'array' },
  { contentType: 'classroom-timer-sounds', payloadShape: 'array' },
  { contentType: 'classroom-timer-presets', payloadShape: 'array' },
  { contentType: 'classroom-timer-tips', payloadShape: 'array' },
  { contentType: 'classroom-strategies', payloadShape: 'array' },
  { contentType: 'sign-language-items', payloadShape: 'array' },
  { contentType: 'sign-language-page-content', payloadShape: 'object' },
  { contentType: 'regulation-zones', payloadShape: 'array' },
  { contentType: 'zones-of-regulation-page-content', payloadShape: 'object' },
  { contentType: 'dashboard-encouraging-quotes', payloadShape: 'array' },
  { contentType: 'dashboard-compliance-items', payloadShape: 'array' },
  { contentType: 'dashboard-sign-of-week', payloadShape: 'object' },
];

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function getStringField(value: unknown, fieldName: string): string {
  if (!isRecord(value)) {
    throw new Error(`Payload item must be an object with ${fieldName}`);
  }

  const fieldValue = value[fieldName];

  if (typeof fieldValue !== 'string') {
    throw new Error(`Payload item ${fieldName} must be a string`);
  }

  return fieldValue;
}

function parseNamedItems(payload: unknown, fieldName: string): readonly { readonly value: string }[] {
  if (!Array.isArray(payload)) {
    throw new Error('Content catalog payload must be an array');
  }

  return payload.map((item) => ({
    value: getStringField(item, fieldName),
  }));
}

async function getSeededPayload(
  request: APIRequestContext,
  contentType: string,
): Promise<unknown> {
  const response = await request.get(
    `${BACKEND_API_URL}/public/content-catalog/${encodeURIComponent(contentType)}`,
  );

  expect(
    response.ok(),
    `Seeded backend content catalog request failed for ${contentType}. Run backend migrations, seeders, and backend server before this suite.`,
  ).toBe(true);

  const catalog: unknown = await response.json();
  if (!isRecord(catalog)) {
    throw new Error(`Content catalog response for ${contentType} must be an object`);
  }

  return catalog.payload;
}

function getFirstPayloadItem<TItem>(payload: readonly TItem[], contentType: string): TItem {
  expect(payload.length, `${contentType} seed payload must contain at least one item`).toBeGreaterThan(0);
  return payload[0];
}

function expectSeededPayloadShape(payload: unknown, expectation: SeededContentTypeExpectation) {
  if (expectation.payloadShape === 'array') {
    if (!Array.isArray(payload)) {
      throw new Error(`${expectation.contentType} payload must be an array`);
    }

    expect(payload.length, `${expectation.contentType} payload must contain at least one item`).toBeGreaterThan(0);
    return;
  }

  if (!isRecord(payload)) {
    throw new Error(`${expectation.contentType} payload must be an object`);
  }
}

test.describe('seeded content catalog integration', () => {
  test('minimum backend content seed set is available through public API', async ({ request }) => {
    for (const expectation of MINIMUM_SEEDED_CONTENT_TYPES) {
      const payload = await getSeededPayload(request, expectation.contentType);
      expectSeededPayloadShape(payload, expectation);
    }
  });

  test('renders seeded classroom timer catalog content', async ({ page, request }) => {
    const payload = await getSeededPayload(request, 'classroom-timer-sounds');
    const sounds: readonly TimerSoundPayload[] = parseNamedItems(payload, 'name').map((item) => ({
      name: item.value,
    }));
    const firstSound = getFirstPayloadItem<TimerSoundPayload>(sounds, 'classroom-timer-sounds');

    await authenticateViaPage(page);
    await page.goto('/classroom-timer');

    await expect(page.getByText('Loading classroom timer content...')).toHaveCount(0);
    await expect(page.getByRole('heading', { name: 'Classroom Timer' })).toBeVisible();
    await expect(page.getByText(firstSound.name, { exact: true })).toBeVisible();
  });

  test('renders seeded classroom support strategies', async ({ page, request }) => {
    const payload = await getSeededPayload(request, 'classroom-strategies');
    const strategies: readonly ClassroomStrategyPayload[] = parseNamedItems(payload, 'title').map((item) => ({
      title: item.value,
    }));
    const firstStrategy = getFirstPayloadItem<ClassroomStrategyPayload>(strategies, 'classroom-strategies');

    await authenticateViaPage(page);
    await page.goto('/classroom-support');

    await expect(page.getByText('Loading classroom strategies...')).toHaveCount(0);
    await expect(page.getByRole('heading', { name: 'Classroom Support' })).toBeVisible();
    await expect(page.getByText(firstStrategy.title, { exact: true })).toBeVisible();
  });

  test('renders seeded sign language content', async ({ page, request }) => {
    const payload = await getSeededPayload(request, 'sign-language-items');
    const signs: readonly SignLanguagePayload[] = parseNamedItems(payload, 'word').map((item) => ({
      word: item.value,
    }));
    const firstSign = getFirstPayloadItem<SignLanguagePayload>(signs, 'sign-language-items');

    await authenticateViaPage(page);
    await page.goto('/sign-language');

    await expect(page.getByText('Loading sign language content and saved progress.')).toHaveCount(0);
    await expect(page.getByRole('heading', { name: 'Sign Language' })).toBeVisible();
    await expect(page.getByText(firstSign.word, { exact: true })).toBeVisible();
  });

  test('renders seeded zones of regulation content', async ({ page, request }) => {
    const payload = await getSeededPayload(request, 'regulation-zones');
    const zones: readonly ZonePayload[] = parseNamedItems(payload, 'name').map((item) => ({
      name: item.value,
    }));
    const firstZone = getFirstPayloadItem<ZonePayload>(zones, 'regulation-zones');

    await authenticateViaPage(page);
    await page.goto('/zones-of-regulation');

    await expect(page.getByText('Loading regulation zone content.')).toHaveCount(0);
    await expect(page.getByRole('heading', { name: 'Zones of Regulation' })).toBeVisible();
    await expect(page.getByText(firstZone.name, { exact: true })).toBeVisible();
  });
});
