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

type RequestCapture = {
  loginBody?: Record<string, unknown>;
  resetBody?: Record<string, unknown>;
  resetToken?: string | null;
  clientLogs: string[];
};

const resetNewPasswordName = /^(새 비밀번호|ui\.userfront\.reset\.new_password)$/;
const resetConfirmPasswordName =
  /^(새 비밀번호 확인|ui\.userfront\.reset\.confirm_password)$/;
const resetSubmitButtonName = /^(비밀번호 변경|ui\.userfront\.reset\.submit)$/;

async function enableFlutterAccessibility(page: Page): Promise<void> {
  const button = page.getByRole('button', { name: 'Enable accessibility' });
  if (await button.count()) {
    await button.first().evaluate((node) => {
      (node as HTMLElement).click();
    });
    await page.waitForTimeout(200);
    return;
  }
  await page.waitForTimeout(300);
  const placeholder = page.locator('flt-semantics-placeholder').first();
  if (await placeholder.count()) {
    await placeholder.evaluate((node) => {
      (node as HTMLElement).click();
    });
    await page.waitForTimeout(800);
  }
}

type ScreenCoords = {
  signinPasswordTabX: number;
  signinTabY: number;
  signinLoginIdX: number;
  signinLoginIdY: number;
  signinPasswordX: number;
  signinPasswordY: number;
  signinSubmitX: number;
  signinSubmitY: number;
  resetNewPasswordX: number;
  resetNewPasswordY: number;
  resetConfirmPasswordX: number;
  resetConfirmPasswordY: number;
  resetSubmitX: number;
  resetSubmitY: number;
};

const desktopCoords: ScreenCoords = {
  signinPasswordTabX: 522,
  signinTabY: 158,
  signinLoginIdX: 640,
  signinLoginIdY: 245,
  signinPasswordX: 640,
  signinPasswordY: 311,
  signinSubmitX: 640,
  signinSubmitY: 381,
  resetNewPasswordX: 640,
  resetNewPasswordY: 382,
  resetConfirmPasswordX: 640,
  resetConfirmPasswordY: 464,
  resetSubmitX: 640,
  resetSubmitY: 534,
};

const mobileCoords: ScreenCoords = {
  signinPasswordTabX: 90,
  signinTabY: 158,
  signinLoginIdX: 206,
  signinLoginIdY: 268,
  signinPasswordX: 206,
  signinPasswordY: 334,
  signinSubmitX: 206,
  signinSubmitY: 399,
  resetNewPasswordX: 206,
  resetNewPasswordY: 382,
  resetConfirmPasswordX: 206,
  resetConfirmPasswordY: 464,
  resetSubmitX: 206,
  resetSubmitY: 534,
};

function coordsFor(page: Page): ScreenCoords {
  const viewport = page.viewportSize();
  return (viewport?.width ?? 1280) <= 500 ? mobileCoords : desktopCoords;
}

function isMobileProject(page: Page): boolean {
  const viewport = page.viewportSize();
  return (viewport?.width ?? 1280) <= 500;
}

async function clickPasswordTab(page: Page): Promise<void> {
  if (isMobileProject(page)) {
    return;
  }
  const coords = coordsFor(page);
  await page.waitForTimeout(900);
  const pane = page.locator('flt-glass-pane');
  await pane.click({
    position: { x: coords.signinPasswordTabX, y: coords.signinTabY },
    force: true,
  });
  await page.waitForTimeout(120);
  await pane.click({
    position: { x: coords.signinPasswordTabX, y: coords.signinTabY },
    force: true,
  });
  await page.waitForTimeout(200);
}

async function fillAt(page: Page, x: number, y: number, value: string): Promise<void> {
  const pane = page.locator('flt-glass-pane');
  await pane.click({ position: { x, y }, force: true });
  await page.waitForTimeout(100);
  await page.keyboard.press('Control+A');
  await page.keyboard.press('Backspace');
  await page.keyboard.type(value);
}

async function typeIntoAccessibleField(
  page: Page,
  field: Locator,
  value: string,
): Promise<void> {
  await field.click({ force: true });
  await page.waitForTimeout(100);
  await page.keyboard.press('Control+A');
  await page.keyboard.press('Backspace');
  await page.keyboard.type(value);
}

async function fillPasswordLoginForm(
  page: Page,
  loginId: string,
  password: string,
): Promise<void> {
  if (isMobileProject(page)) {
    await enableFlutterAccessibility(page);
    const inputs = page.getByRole('textbox');
    await inputs.nth(0).fill(loginId);
    await inputs.nth(1).fill(password);
    return;
  }
  const coords = coordsFor(page);
  await fillAt(page, coords.signinLoginIdX, coords.signinLoginIdY, loginId);
  await fillAt(page, coords.signinPasswordX, coords.signinPasswordY, password);
}

async function submitPasswordLogin(page: Page): Promise<void> {
  if (isMobileProject(page)) {
    await enableFlutterAccessibility(page);
    await page.getByRole('button', { name: '로그인' }).click({ force: true });
    return;
  }
  await page.keyboard.press('Enter');
}

async function fillResetPasswordForm(page: Page, password: string): Promise<void> {
  await enableFlutterAccessibility(page);
  const newPasswordInput = page.getByRole('textbox', {
    name: resetNewPasswordName,
  });
  const confirmPasswordInput = page.getByRole('textbox', {
    name: resetConfirmPasswordName,
  });
  if ((await newPasswordInput.count()) > 0 && (await confirmPasswordInput.count()) > 0) {
    await typeIntoAccessibleField(page, newPasswordInput, password);
    await typeIntoAccessibleField(page, confirmPasswordInput, password);
    return;
  }
  if (isMobileProject(page)) {
    await page.getByRole('textbox', { name: resetNewPasswordName }).fill(password);
    await page.getByRole('textbox', { name: resetConfirmPasswordName }).fill(password);
    return;
  }
  const coords = coordsFor(page);
  await fillAt(page, coords.resetNewPasswordX, coords.resetNewPasswordY, password);
  await fillAt(
    page,
    coords.resetConfirmPasswordX,
    coords.resetConfirmPasswordY,
    password,
  );
}

async function submitResetPassword(page: Page): Promise<void> {
  await enableFlutterAccessibility(page);
  const submitButton = page.getByRole('button', { name: resetSubmitButtonName });
  if ((await submitButton.count()) > 0) {
    await submitButton.click({ force: true });
    return;
  }
  if (isMobileProject(page)) {
    return;
  }
  const coords = coordsFor(page);
  await page.locator('flt-glass-pane').click({
    position: { x: coords.resetSubmitX, y: coords.resetSubmitY },
    force: true,
  });
}

async function mockAuthApis(page: Page, capture: RequestCapture): Promise<void> {
  await page.route('**/api/v1/**', async (route: Route) => {
    const requestUrl = new URL(route.request().url());
    const path = requestUrl.pathname;

    if (path.endsWith('/api/v1/auth/password/login')) {
      capture.loginBody = (route.request().postDataJSON() ?? {}) as Record<
        string,
        unknown
      >;

      const loginId = String(capture.loginBody.loginId ?? '');
      const password = String(capture.loginBody.password ?? '');
      if (loginId === 'e2e@example.com' && password === 'ValidPass1!') {
        await route.fulfill({
          status: 200,
          contentType: 'application/json',
          body: JSON.stringify({
            sessionJwt: 'e30.e30.e30',
            provider: 'ory',
          }),
        });
        return;
      }

      await route.fulfill({
        status: 401,
        contentType: 'application/json',
        body: JSON.stringify({ error: 'password_or_email_mismatch' }),
      });
      return;
    }

    if (path.endsWith('/api/v1/auth/password/policy')) {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({
          minLength: 12,
          minCharacterTypes: 3,
          lowercase: true,
          uppercase: true,
          number: true,
          nonAlphanumeric: true,
        }),
      });
      return;
    }

    if (path.endsWith('/api/v1/auth/password/reset/complete')) {
      capture.resetBody = (route.request().postDataJSON() ?? {}) as Record<
        string,
        unknown
      >;
      capture.resetToken = requestUrl.searchParams.get('token');
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ status: 'ok' }),
      });
      return;
    }

    if (path.endsWith('/api/v1/client-log')) {
      const payload = (route.request().postDataJSON() ?? {}) as {
        message?: string;
      };
      if (payload.message != null) {
        capture.clientLogs.push(payload.message);
      }
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ ok: true }),
      });
      return;
    }

    if (path.endsWith('/api/v1/user/me')) {
      const authHeader = route.request().headers()['authorization'] ?? '';
      if (!authHeader.startsWith('Bearer ')) {
        await route.fulfill({
          status: 401,
          contentType: 'application/json',
          body: JSON.stringify({ error: 'unauthorized' }),
        });
        return;
      }

      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({
          id: 'e2e-user',
          email: 'e2e@example.com',
          name: 'E2E User',
          phone: '+821012341234',
          department: 'QA',
          affiliationType: 'employee',
          companyCode: 'BARON',
          tenant: {
            id: 'tenant-1',
            name: 'Baron',
            slug: 'baron',
            description: 'E2E tenant',
          },
        }),
      });
      return;
    }

    if (path.endsWith('/api/v1/user/rp/linked')) {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ items: [] }),
      });
      return;
    }

    if (path.endsWith('/api/v1/audit/auth/timeline')) {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify({ items: [], next_cursor: '' }),
      });
      return;
    }

    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify({}),
    });
  });
}

test.describe('UserFront WASM password login and reset', () => {
  test.skip(({ isMobile }) => isMobile, 'Desktop only (hardcoded coordinates)');
  test('비밀번호 로그인 성공 시 dashboard로 이동하고 토큰을 저장한다', async ({ page }) => {
    test.skip(
      isMobileProject(page),
      'Mobile webapp keeps dedicated auth-routing coverage; password form canvas automation is covered on desktop.',
    );
    const capture: RequestCapture = { clientLogs: [] };
    await mockAuthApis(page, capture);

    await page.goto('/ko/signin');
    await clickPasswordTab(page);
    await fillPasswordLoginForm(page, 'e2e@example.com', 'ValidPass1!');
    await submitPasswordLogin(page);

    await expect(page).toHaveURL(/\/ko\/dashboard$/);

    expect(capture.loginBody?.loginId).toBe('e2e@example.com');
    expect(capture.loginBody?.password).toBe('ValidPass1!');

    const storedToken = await page.evaluate(() =>
      window.localStorage.getItem('baron_auth_token'),
    );
    expect(storedToken).toBe('e30.e30.e30');
  });

  test('비밀번호 로그인 실패 시 에러 코드를 사용자에게 표시한다', async ({ page }) => {
    test.skip(
      isMobileProject(page),
      'Mobile webapp keeps dedicated auth-routing coverage; password form canvas automation is covered on desktop.',
    );
    const capture: RequestCapture = { clientLogs: [] };
    await mockAuthApis(page, capture);

    await page.goto('/ko/signin');
    await clickPasswordTab(page);
    await fillPasswordLoginForm(page, 'e2e@example.com', 'WrongPass1!');
    await submitPasswordLogin(page);

    await expect(page).toHaveURL(/\/ko\/signin$/);
    await expect
      .poll(
        () =>
          capture.clientLogs.some((message) =>
            message.includes('password_or_email_mismatch'),
          ),
        { timeout: 10000 },
      )
      .toBe(true);
  });

  test('reset-password에서 변경 성공 시 signin으로 이동한다', async ({ page }) => {
    const capture: RequestCapture = { clientLogs: [] };
    await mockAuthApis(page, capture);

    const policyLoaded = page.waitForResponse(
      (response) =>
        response.url().includes('/api/v1/auth/password/policy') &&
        response.status() === 200,
    );
    await page.goto('/ko/reset-password?token=reset-token-e2e');
    await policyLoaded;
    await page.waitForTimeout(900);
    await fillResetPasswordForm(page, 'ValidPass1!A');
    await submitResetPassword(page);

    await expect
      .poll(
        () => capture.resetBody?.newPassword as string | undefined,
        { timeout: 10000 },
      )
      .toBe('ValidPass1!A');
    await expect(page).toHaveURL(/\/ko\/signin(?:\?.*)?$/, { timeout: 10_000 });
    expect(capture.resetToken).toBe('reset-token-e2e');
    expect(capture.resetBody?.newPassword).toBe('ValidPass1!A');
  });
});
