import { test, expect } from "@playwright/test";

function parsePx(value: string): number {
  const n = parseFloat(value || "");
  return Number.isFinite(n) ? n : 0;
}

test.beforeEach(async ({ page }) => {
  await page.route("**/api/auth/me", async (route) => {
    await route.fulfill({
      status: 200,
      contentType: "application/json",
      body: JSON.stringify({ id: "user-form-parity", email: "form-parity@example.com", tokenVersion: 1 }),
    });
  });
});

test("formInput: 에디터 캔버스와 프리뷰에서 높이/패딩/색상이 동일해야 한다", async ({ page }) => {
  await page.goto("/editor");

  const canvas = page.getByTestId("editor-canvas");
  const sidebar = page.getByTestId("properties-sidebar");

  await page.getByRole("button", { name: "Input field" }).click();

  await sidebar.getByRole("textbox", { name: "Field label" }).fill("에디터-프리뷰 인풋");
  await sidebar.getByLabel("Text alignment").selectOption("center");
  await sidebar.getByRole("combobox", { name: "Width", exact: true }).selectOption("fixed");
  await sidebar.getByLabel("Field fixed width 커스텀").fill("320");
  await sidebar.getByLabel("Field text color HEX").fill("#ff0000");
  await sidebar.getByLabel("Field fill color HEX").fill("#00ff88");
  await sidebar.getByLabel("Field border color HEX").fill("#0000ff");

  const editorInput = canvas.getByRole("textbox", { name: "에디터-프리뷰 인풋" });
  const editorStyles = await editorInput.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLInputElement);
    return {
      height: s.height,
      paddingTop: s.paddingTop,
      paddingBottom: s.paddingBottom,
      fontSize: s.fontSize,
      color: s.color,
      backgroundColor: s.backgroundColor,
      borderColor: s.borderColor,
      borderRadius: s.borderRadius,
    };
  });

  const editorAttrs = await editorInput.evaluate((el) => {
    const input = el as HTMLInputElement;
    return {
      type: input.type,
      name: input.name,
      placeholder: input.placeholder,
    };
  });

  await Promise.all([
    page.waitForURL(/\/preview/),
    page.getByRole("link", { name: "Open preview" }).click(),
  ]);
  await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();

  const previewInput = page.getByRole("textbox", { name: "에디터-프리뷰 인풋" });
  const previewStyles = await previewInput.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLInputElement);
    return {
      height: s.height,
      paddingTop: s.paddingTop,
      paddingBottom: s.paddingBottom,
      fontSize: s.fontSize,
      color: s.color,
      backgroundColor: s.backgroundColor,
      borderColor: s.borderColor,
      borderRadius: s.borderRadius,
    };
  });

  const editorHeight = parsePx(editorStyles.height);
  const previewHeight = parsePx(previewStyles.height);
  expect(Math.abs(previewHeight - editorHeight)).toBeLessThanOrEqual(2);

  const editorPaddingTop = parsePx(editorStyles.paddingTop);
  const previewPaddingTop = parsePx(previewStyles.paddingTop);
  expect(Math.abs(previewPaddingTop - editorPaddingTop)).toBeLessThanOrEqual(3);

  const editorPaddingBottom = parsePx(editorStyles.paddingBottom);
  const previewPaddingBottom = parsePx(previewStyles.paddingBottom);
  expect(Math.abs(previewPaddingBottom - editorPaddingBottom)).toBeLessThanOrEqual(3);

  expect(previewStyles.fontSize).toBe(editorStyles.fontSize);
  expect(previewStyles.color).toBe(editorStyles.color);
  expect(previewStyles.backgroundColor).toBe(editorStyles.backgroundColor);
  expect(previewStyles.borderColor).toBe(editorStyles.borderColor);
  expect(previewStyles.borderRadius).toBe(editorStyles.borderRadius);

  const previewAttrs = await previewInput.evaluate((el) => {
    const input = el as HTMLInputElement;
    return {
      type: input.type,
      name: input.name,
      placeholder: input.placeholder,
    };
  });

  expect(previewAttrs.type).toBe(editorAttrs.type);
  expect(previewAttrs.placeholder).toBe(editorAttrs.placeholder);
  expect(previewAttrs.name).toBe(editorAttrs.name);
});

test("formSelect: 에디터 캔버스와 프리뷰에서 높이/패딩/색상이 동일해야 한다", async ({ page }) => {
  await page.goto("/editor");

  const canvas = page.getByTestId("editor-canvas");
  const sidebar = page.getByTestId("properties-sidebar");

  await page.getByRole("button", { name: "Select field" }).click();

  await sidebar.getByRole("textbox", { name: "Field label" }).fill("에디터-프리뷰 셀렉트");
  await sidebar.getByLabel("Field width").selectOption("fixed");
  await sidebar.getByLabel("Field fixed width 커스텀").fill("360");
  await sidebar.getByLabel("Select text color HEX").fill("#ff00ff");
  await sidebar.getByLabel("Select fill color HEX").fill("#0033ff");
  await sidebar.getByLabel("Select border color HEX").fill("#00ffaa");

  const editorSelect = canvas.getByRole("combobox", { name: "에디터-프리뷰 셀렉트" });
  const editorStyles = await editorSelect.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLSelectElement);
    return {
      height: s.height,
      paddingTop: s.paddingTop,
      paddingBottom: s.paddingBottom,
      fontSize: s.fontSize,
      color: s.color,
      backgroundColor: s.backgroundColor,
      borderColor: s.borderColor,
      borderRadius: s.borderRadius,
    };
  });

  const editorSelectAttrs = await editorSelect.evaluate((el) => {
    const select = el as HTMLSelectElement;
    return {
      name: select.name,
      value: select.value,
    };
  });

  await Promise.all([
    page.waitForURL(/\/preview/),
    page.getByRole("link", { name: "Open preview" }).click(),
  ]);
  await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();

  const previewSelect = page.getByRole("combobox", { name: "에디터-프리뷰 셀렉트" });
  const previewStyles = await previewSelect.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLSelectElement);
    return {
      height: s.height,
      paddingTop: s.paddingTop,
      paddingBottom: s.paddingBottom,
      fontSize: s.fontSize,
      color: s.color,
      backgroundColor: s.backgroundColor,
      borderColor: s.borderColor,
      borderRadius: s.borderRadius,
    };
  });

  const editorHeight = parsePx(editorStyles.height);
  const previewHeight = parsePx(previewStyles.height);
  expect(Math.abs(previewHeight - editorHeight)).toBeLessThanOrEqual(2);

  const editorPaddingTop = parsePx(editorStyles.paddingTop);
  const previewPaddingTop = parsePx(previewStyles.paddingTop);
  expect(Math.abs(previewPaddingTop - editorPaddingTop)).toBeLessThanOrEqual(3);

  const editorPaddingBottom = parsePx(editorStyles.paddingBottom);
  const previewPaddingBottom = parsePx(previewStyles.paddingBottom);
  expect(Math.abs(previewPaddingBottom - editorPaddingBottom)).toBeLessThanOrEqual(3);

  expect(previewStyles.fontSize).toBe(editorStyles.fontSize);
  expect(previewStyles.color).toBe(editorStyles.color);
  expect(previewStyles.backgroundColor).toBe(editorStyles.backgroundColor);
  expect(previewStyles.borderColor).toBe(editorStyles.borderColor);
  expect(previewStyles.borderRadius).toBe(editorStyles.borderRadius);

  const previewSelectAttrs = await previewSelect.evaluate((el) => {
    const select = el as HTMLSelectElement;
    return {
      name: select.name,
      value: select.value,
    };
  });

  expect(previewSelectAttrs.name).toBe(editorSelectAttrs.name);
  expect(previewSelectAttrs.value).toBe(editorSelectAttrs.value);
});

test("formCheckbox/formRadio: 에디터 캔버스와 프리뷰에서 그룹 너비/라벨 간격/옵션 간격이 동일해야 한다", async ({ page }) => {
  await page.goto("/editor");

  const canvas = page.getByTestId("editor-canvas");
  const sidebar = page.getByTestId("properties-sidebar");

  await page.getByRole("button", { name: "Checkbox group" }).click();

  await sidebar.getByLabel("Group title", { exact: true }).fill("에디터-프리뷰 체크 그룹");
  await sidebar.getByLabel("Group title display mode").selectOption("visible");
  await sidebar.getByLabel(/^Layout/).selectOption("inline");
  await sidebar.getByLabel("Label/field gap 커스텀").fill("32");
  await sidebar.getByLabel("Field width").selectOption("fixed");
  await sidebar.getByLabel("Field fixed width 커스텀").fill("360");
  await sidebar.getByLabel("Checkbox option gap 커스텀").fill("20");
  const editorCheckboxLabel = canvas.getByText("에디터-프리뷰 체크 그룹");
  const editorCheckboxData = await editorCheckboxLabel.evaluate((el) => {
    const groupEl = (el as HTMLElement).parentElement as HTMLElement;
    const groupStyle = window.getComputedStyle(groupEl);
    const optionsEl = groupEl.querySelector(".pb-form-options") as HTMLElement | null;
    const optionsStyle = optionsEl ? window.getComputedStyle(optionsEl) : null;
    const firstOptionInput = optionsEl?.querySelector("input[type='checkbox']") as
      | HTMLInputElement
      | null;
    const firstOption = firstOptionInput
      ? { type: firstOptionInput.type, name: firstOptionInput.name, value: firstOptionInput.value }
      : { type: "", name: "", value: "" };
    return {
      groupWidth: groupStyle.width,
      groupColumnGap: groupStyle.columnGap,
      optionsRowGap: optionsStyle ? optionsStyle.rowGap : "",
      firstOption,
    };
  });

  await page.getByRole("button", { name: "Radio group" }).click();

  await sidebar.getByLabel("Group title", { exact: true }).fill("에디터-프리뷰 라디오 그룹");
  await sidebar.getByLabel("Group title display mode").selectOption("visible");
  await sidebar.getByLabel("Group title layout").selectOption("inline");
  await sidebar.getByLabel("Label/field gap 커스텀").fill("32");
  await sidebar.getByLabel("Field width").selectOption("fixed");
  await sidebar.getByLabel("Field fixed width 커스텀").fill("360");
  await sidebar.getByLabel("Radio option gap 커스텀").fill("20");
  const editorRadioLabel = canvas.getByText("에디터-프리뷰 라디오 그룹");
  const editorRadioData = await editorRadioLabel.evaluate((el) => {
    const groupEl = (el as HTMLElement).parentElement as HTMLElement;
    const groupStyle = window.getComputedStyle(groupEl);
    const optionsEl = groupEl.querySelector(".pb-form-options") as HTMLElement | null;
    const optionsStyle = optionsEl ? window.getComputedStyle(optionsEl) : null;
    const firstOptionInput = optionsEl?.querySelector("input[type='radio']") as
      | HTMLInputElement
      | null;
    const firstOption = firstOptionInput
      ? { type: firstOptionInput.type, name: firstOptionInput.name, value: firstOptionInput.value }
      : { type: "", name: "", value: "" };
    return {
      groupWidth: groupStyle.width,
      groupColumnGap: groupStyle.columnGap,
      optionsRowGap: optionsStyle ? optionsStyle.rowGap : "",
      firstOption,
    };
  });

  await Promise.all([
    page.waitForURL(/\/preview/),
    page.getByRole("link", { name: "Open preview" }).click(),
  ]);
  await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();

  const previewCheckboxGroup = page.getByTestId("preview-form-checkbox-group").first();
  const previewCheckboxGroupStyles = await previewCheckboxGroup.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLElement);
    return {
      width: s.width,
      columnGap: s.columnGap,
    };
  });

  const previewCheckboxFirstOption = await previewCheckboxGroup.evaluate((el) => {
    const input = (el as HTMLElement).querySelector("input[type='checkbox']") as
      | HTMLInputElement
      | null;
    return input
      ? { type: input.type, name: input.name, value: input.value }
      : { type: "", name: "", value: "" };
  });

  const previewCheckboxOptions = previewCheckboxGroup
    .getByTestId("preview-form-checkbox-options")
    .first();
  const previewCheckboxOptionsStyles = await previewCheckboxOptions.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLElement);
    return {
      rowGap: s.rowGap,
    };
  });

  const previewRadioGroup = page.getByTestId("preview-form-radio-group").first();
  const previewRadioGroupStyles = await previewRadioGroup.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLElement);
    return {
      width: s.width,
      columnGap: s.columnGap,
    };
  });

  const previewRadioFirstOption = await previewRadioGroup.evaluate((el) => {
    const input = (el as HTMLElement).querySelector("input[type='radio']") as
      | HTMLInputElement
      | null;
    return input
      ? { type: input.type, name: input.name, value: input.value }
      : { type: "", name: "", value: "" };
  });

  const previewRadioOptions = previewRadioGroup
    .getByTestId("preview-form-radio-options")
    .first();
  const previewRadioOptionsStyles = await previewRadioOptions.evaluate((el) => {
    const s = window.getComputedStyle(el as HTMLElement);
    return {
      rowGap: s.rowGap,
    };
  });

  const editorCheckboxWidth = parsePx(editorCheckboxData.groupWidth);
  const previewCheckboxWidth = parsePx(previewCheckboxGroupStyles.width);
  expect(Math.abs(previewCheckboxWidth - editorCheckboxWidth)).toBeLessThanOrEqual(96);

  const editorCheckboxLabelGap = parsePx(editorCheckboxData.groupColumnGap);
  const previewCheckboxLabelGap = parsePx(previewCheckboxGroupStyles.columnGap);
  expect(Math.abs(previewCheckboxLabelGap - editorCheckboxLabelGap)).toBeLessThanOrEqual(8);

  const editorCheckboxOptionGap = parsePx(editorCheckboxData.optionsRowGap);
  const previewCheckboxOptionGap = parsePx(previewCheckboxOptionsStyles.rowGap);
  expect(Math.abs(previewCheckboxOptionGap - editorCheckboxOptionGap)).toBeLessThanOrEqual(8);

  const editorRadioWidth = parsePx(editorRadioData.groupWidth);
  const previewRadioWidth = parsePx(previewRadioGroupStyles.width);
  expect(Math.abs(previewRadioWidth - editorRadioWidth)).toBeLessThanOrEqual(96);

  const editorRadioLabelGap = parsePx(editorRadioData.groupColumnGap);
  const previewRadioLabelGap = parsePx(previewRadioGroupStyles.columnGap);
  expect(Math.abs(previewRadioLabelGap - editorRadioLabelGap)).toBeLessThanOrEqual(2);

  const editorRadioOptionGap = parsePx(editorRadioData.optionsRowGap);
  const previewRadioOptionGap = parsePx(previewRadioOptionsStyles.rowGap);
  expect(Math.abs(previewRadioOptionGap - editorRadioOptionGap)).toBeLessThanOrEqual(8);

  expect(editorCheckboxData.firstOption.type).toBe("checkbox");
  expect(previewCheckboxFirstOption.type).toBe(editorCheckboxData.firstOption.type);
  expect(previewCheckboxFirstOption.name).toBe(editorCheckboxData.firstOption.name);
  expect(previewCheckboxFirstOption.value).toBe(editorCheckboxData.firstOption.value);

  expect(editorRadioData.firstOption.type).toBe("radio");
  expect(previewRadioFirstOption.type).toBe(editorRadioData.firstOption.type);
  expect(previewRadioFirstOption.name).toBe(editorRadioData.firstOption.name);
  expect(previewRadioFirstOption.value).toBe(editorRadioData.firstOption.value);
});
