import { describe, it, expect } from "vitest";
import { expectNoPxInStyleObject, expectNoPxInStyleParts } from "./styleInvariants";
import {
  computeListExportTokens,
  computeListEditorTokens,
  computeListPublicTokens,
} from "@/features/editor/utils/listHelpers";
import type { ListBlockProps } from "@/features/editor/state/editorStore";

const baseProps: ListBlockProps = {
  items: [],
  ordered: false,
  align: "left",
};

describe("listHelpers.computeListExportTokens", () => {
  it("기본 unordered 리스트는 Tag=ul, left 정렬, items 플랫닝을 수행해야 한다", () => {
    const props: ListBlockProps = {
      ...baseProps,
      items: ["  first  ", "", " second"],
    };

    const tokens = computeListExportTokens(props);

    expect(tokens.Tag).toBe("ul");
    expect(tokens.items).toEqual(["  first  ", " second"]);
    expect(tokens.listStyleParts).toContain("text-align:left");
  });

  it("ordered=true 인 경우 Tag=ol 이어야 한다", () => {
    const props: ListBlockProps = {
      ...baseProps,
      ordered: true,
      items: ["one", "two"],
    };

    const tokens = computeListExportTokens(props);

    expect(tokens.Tag).toBe("ol");
    expect(tokens.items).toEqual(["one", "two"]);
  });

  it("itemsTree 가 있으면 중첩 트리를 DFS 순서로 평탄화해야 한다", () => {
    const props: ListBlockProps = {
      ...baseProps,
      itemsTree: [
        {
          id: "n1",
          text: "parent",
          children: [
            { id: "n1-1", text: "child-1", children: [] },
            { id: "n1-2", text: "child-2", children: [] },
          ],
        },
      ],
    };

    const tokens = computeListExportTokens(props);

    expect(tokens.items).toEqual(["parent", "child-1", "child-2"]);
  });

  it("align 값에 따라 text-align 스타일이 left/center/right 로 설정되어야 한다", () => {
    const centerTokens = computeListExportTokens({ ...baseProps, align: "center", items: ["a"] });
    const rightTokens = computeListExportTokens({ ...baseProps, align: "right", items: ["a"] });

    expect(centerTokens.listStyleParts).toContain("text-align:center");
    expect(rightTokens.listStyleParts).toContain("text-align:right");
  });

  it("backgroundColorCustom 이 있으면 background-color 스타일을 추가해야 한다", () => {
    const tokens = computeListExportTokens({
      ...baseProps,
      items: ["a"],
      backgroundColorCustom: "  #ff0000  ",
    });

    expect(tokens.listStyleParts).toContain("background-color:#ff0000");
  });

  it("items 와 itemsTree 가 모두 비어 있으면 items 배열은 빈 배열이어야 한다", () => {
    const tokens = computeListExportTokens({ ...baseProps });
    expect(tokens.items).toEqual([]);
  });

  it("Export 리스트 listStyleParts 에는 px 단위 값이 포함되지 않아야 한다", () => {
    const tokens = computeListExportTokens({
      ...baseProps,
      align: "center",
      items: ["a"],
      backgroundColorCustom: "#123456",
    });

    expectNoPxInStyleParts(tokens.listStyleParts);
  });
});

describe("listHelpers.computeListEditorTokens", () => {
  it("gapY 토큰에 따라 gapPx 가 sm=4, md=8, lg=16 으로 계산되어야 한다", () => {
    const sm = computeListEditorTokens({ ...baseProps, gapY: "sm" });
    const md = computeListEditorTokens({ ...baseProps, gapY: "md" });
    const lg = computeListEditorTokens({ ...baseProps, gapY: "lg" });

    expect(sm.gapPx).toBe(4);
    expect(md.gapPx).toBe(8);
    expect(lg.gapPx).toBe(16);
  });

  it("gapYPx 가 있으면 gapY 토큰보다 우선해서 gapPx 를 설정해야 한다", () => {
    const tokens = computeListEditorTokens({ ...baseProps, gapY: "sm", gapYPx: 24 });

    expect(tokens.gapPx).toBe(24);
  });

  it("fontSizeCustom / lineHeightCustom / textColorCustom 이 listStyle 에 반영되어야 한다", () => {
    const tokens = computeListEditorTokens({
      ...baseProps,
      fontSizeCustom: "18px",
      lineHeightCustom: "1.8",
      textColorCustom: "#ff0000",
    });

    expect(tokens.listStyle.fontSize).toBe("18px");
    expect(tokens.listStyle.lineHeight).toBe("1.8");
    expect(tokens.listStyle.color).toBe("#ff0000");
  });

  it("bulletStyle 은 bulletStyle 지정값 또는 ordered 여부에 따라 결정되어야 한다", () => {
    const defaultDisc = computeListEditorTokens({
      ...baseProps,
      ordered: false,
      bulletStyle: undefined,
    });

    const defaultDecimal = computeListEditorTokens({
      ...baseProps,
      ordered: true,
      bulletStyle: undefined,
    });

    const none = computeListEditorTokens({
      ...baseProps,
      bulletStyle: "none",
    });

    expect(defaultDisc.bulletStyle).toBe("disc");
    expect(defaultDecimal.bulletStyle).toBe("decimal");
    expect(none.bulletStyle).toBe("none");
  });
});

describe("listHelpers.computeListPublicTokens", () => {
  it("align 값에 따라 alignClass 가 text-left/center/right 로 설정되어야 한다", () => {
    const leftTokens = computeListPublicTokens({ ...baseProps, align: "left" });
    const centerTokens = computeListPublicTokens({ ...baseProps, align: "center" });
    const rightTokens = computeListPublicTokens({ ...baseProps, align: "right" });

    expect(leftTokens.alignClass).toBe("text-left");
    expect(centerTokens.alignClass).toBe("text-center");
    expect(rightTokens.alignClass).toBe("text-right");
  });

  it("gapYPx 와 gapY 토큰에 따라 gapEm 이 px/16 값으로 계산되어야 한다", () => {
    const byPx = computeListPublicTokens({ ...baseProps, gapYPx: 24 });
    const byTokenSm = computeListPublicTokens({ ...baseProps, gapY: "sm" });
    const byTokenLg = computeListPublicTokens({ ...baseProps, gapY: "lg" });

    expect(byPx.gapEm).toBe(24 / 16);
    expect(byTokenSm.gapEm).toBe(4 / 16);
    expect(byTokenLg.gapEm).toBe(16 / 16);
  });

  it("fontSizeCustom / lineHeightCustom 이 px 인 경우 em 단위로 변환되어야 하고, backgroundColorCustom 은 trim 되어야 한다", () => {
    const tokens = computeListPublicTokens({
      ...baseProps,
      fontSizeCustom: "32px",
      lineHeightCustom: "24px",
      backgroundColorCustom: "  #123456  ",
    });

    expect(tokens.listStyle.fontSize).toBe("2em");
    expect(tokens.listStyle.lineHeight).toBe("1.5em");
    expect(tokens.listStyle.backgroundColor).toBe("#123456");
  });

  it("lineHeightCustom 이 px 인 경우 em 단위로 변환되어야 한다", () => {
    const tokens = computeListPublicTokens({
      ...baseProps,
      lineHeightCustom: "24px",
    });

    expect(tokens.listStyle.lineHeight).toBe("1.5em");
  });

  it("bulletStyle 은 bulletStyle 지정값 또는 ordered 여부에 따라 결정되어야 한다", () => {
    const defaultDisc = computeListPublicTokens({ ...baseProps, ordered: false, bulletStyle: undefined });
    const defaultDecimal = computeListPublicTokens({ ...baseProps, ordered: true, bulletStyle: undefined });
    const none = computeListPublicTokens({ ...baseProps, bulletStyle: "none" });

    expect(defaultDisc.bulletStyle).toBe("disc");
    expect(defaultDecimal.bulletStyle).toBe("decimal");
    expect(none.bulletStyle).toBe("none");
  });

  it("퍼블릭 리스트 style 에 px 단위 값이 포함되지 않아야 한다", () => {
    const tokens = computeListPublicTokens({
      ...baseProps,
      fontSizeCustom: "32px",
      lineHeightCustom: "24px",
      textColorCustom: "#ff0000",
      backgroundColorCustom: "#123456",
    });

    expectNoPxInStyleObject(tokens.listStyle);
  });
});
