/**
 * Grid component unit tests
 *
 * This test suite verifies the rendering, props, and className logic of the Grid component.
 * It checks default rendering, children rendering, alignment, gap props, and className merging.
 * Also includes complex scenarios with multiple GridCol components and various size combinations.
 * Tests both React component usage and equivalent HTML structure generation.
 */

import { render } from "@testing-library/react";
import { Grid } from "../Grid";
import { GridCol } from "../GridCol";

const alignment = {
  vertical: ["stretch", "start", "end", "center"],
  horizontal: ["end", "center", "space-around", "space-between"],
};

describe("Grid", () => {
  it("renders with default class grid", () => {
    const { getByTestId } = render(<Grid data-testid="test-id" />);
    expect(getByTestId("test-id")).toHaveClass("grid");
  });

  it("renders children", () => {
    const { getByText } = render(<Grid>test</Grid>);
    expect(getByText("test")).toBeInTheDocument();
  });

  alignment.horizontal.forEach((hAlign) => {
    it(`applies justify-content-${hAlign} class when hAlign is ${hAlign}`, () => {
      const { getByTestId } = render(
        <Grid data-testid="test-id" hAlign={hAlign} />,
      );
      expect(getByTestId("test-id")).toHaveClass(`justify-content-${hAlign}`);
    });
  });

  alignment.vertical.forEach((vAlign) => {
    it(`applies align-items-${vAlign} class when vAlign is ${vAlign}`, () => {
      const { getByTestId } = render(
        <Grid data-testid="test-id" vAlign={vAlign} />,
      );
      expect(getByTestId("test-id")).toHaveClass(`align-items-${vAlign}`);
    });
  });

  it("applies grid--justify-height class when justifyHeight is true", () => {
    const { getByTestId } = render(
      <Grid data-testid="test-id" justifyHeight={true} />,
    );
    expect(getByTestId("test-id")).toHaveClass("grid--justify-height");
  });

  ["none", "xsmall", "small", "medium", "large", "xlarge"].forEach(
    (rowGapSize) => {
      it(`applies grid-row-gap--${rowGapSize} class when rowGapSize is ${rowGapSize}`, () => {
        const { getByTestId } = render(
          <Grid data-testid="test-id" rowGapSize={rowGapSize} />,
        );
        expect(getByTestId("test-id")).toHaveClass(
          `grid-row-gap--${rowGapSize}`,
        );
      });
    },
  );

  ["small", "large", "xlarge", "none"].forEach((columnGapSize) => {
    it(`applies grid-column-gap--${columnGapSize} class when columnGapSize is ${columnGapSize}`, () => {
      const { getByTestId } = render(
        <Grid data-testid="test-id" columnGapSize={columnGapSize} />,
      );
      expect(getByTestId("test-id")).toHaveClass(
        `grid-column-gap--${columnGapSize}`,
      );
    });
  });

  it("applies responsive row gap classes when rowGapSize is object", () => {
    const { getByTestId } = render(
      <Grid
        data-testid="test-id"
        rowGapSize={{ xs: "medium", md: "xlarge" }}
      />,
    );
    expect(getByTestId("test-id")).toHaveClass(
      "grid-row-gap--medium",
      "grid-row-gap--md-xlarge",
    );
  });

  it("applies responsive column gap classes when columnGapSize is object", () => {
    const { getByTestId } = render(
      <Grid
        data-testid="test-id"
        columnGapSize={{ xs: "small", lg: "xlarge" }}
      />,
    );
    expect(getByTestId("test-id")).toHaveClass(
      "grid-column-gap--small",
      "grid-column-gap--lg-xlarge",
    );
  });

  it("merges additional className", () => {
    const { getByTestId } = render(
      <Grid data-testid="test-id" className="test-class" />,
    );
    expect(getByTestId("test-id")).toHaveClass("test-class");
  });

  it("spreads additional props to the rendered element", () => {
    const { getByTestId } = render(
      <Grid data-testid="test-id" id="my-grid" data-extra="foo" />,
    );
    const el = getByTestId("test-id");
    expect(el).toHaveAttribute("id", "my-grid");
    expect(el).toHaveAttribute("data-extra", "foo");
  });
});

describe("Grid with GridCol combinations", () => {
  it("renders default GridCol with 12-column layout", () => {
    const { container } = render(
      <Grid>
        <GridCol>default 12</GridCol>
        <GridCol size={6}>6</GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];

    expect(cols).toHaveLength(2);
    expect(cols[0]).toHaveClass("grid__col");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--6");
  });

  it("renders auto-sized GridCol with fixed columns", () => {
    const { container } = render(
      <Grid>
        <GridCol size="auto">auto</GridCol>
        <GridCol size={4}>4</GridCol>
        <GridCol size={6}>6</GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    expect(cols[0]).toHaveClass("grid__col", "grid__col--auto");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--4");
    expect(cols[2]).toHaveClass("grid__col", "grid__col--6");
  });

  it("renders fill-sized GridCol with fixed columns", () => {
    const { container } = render(
      <Grid>
        <GridCol size="fill">fill</GridCol>
        <GridCol size={4}>4</GridCol>
        <GridCol size={6}>6</GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    expect(cols[0]).toHaveClass("grid__col", "grid__col--fill");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--4");
    expect(cols[2]).toHaveClass("grid__col", "grid__col--6");
  });

  it("renders shrink-sized GridCol with fixed column", () => {
    const { container } = render(
      <Grid>
        <GridCol size="shrink">shrink</GridCol>
        <GridCol size={6}>6</GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    expect(cols[0]).toHaveClass("grid__col", "grid__col--shrink");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--6");
  });

  it("renders complex layout with mixed sizes and alignment", () => {
    const { container } = render(
      <Grid hAlign="center" vAlign="stretch">
        <GridCol size={3} align="top">
          top aligned
        </GridCol>
        <GridCol size="auto" align="middle">
          auto middle
        </GridCol>
        <GridCol size="fill" align="bottom">
          fill bottom
        </GridCol>
        <GridCol size={2} align="stretch">
          stretch
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];

    expect(grid).toHaveClass("justify-content-center", "align-items-stretch");
    expect(cols[0]).toHaveClass("grid__col", "grid__col--3", "align-self-top");
    expect(cols[1]).toHaveClass(
      "grid__col",
      "grid__col--auto",
      "align-self-middle",
    );
    expect(cols[2]).toHaveClass(
      "grid__col",
      "grid__col--fill",
      "align-self-bottom",
    );
    expect(cols[3]).toHaveClass(
      "grid__col",
      "grid__col--2",
      "align-self-stretch",
    );
  });

  it("renders responsive grid with custom tags", () => {
    const { container } = render(
      <Grid tag="section" justifyHeight>
        <GridCol tag="article" size={8}>
          main content
        </GridCol>
        <GridCol tag="aside" size={4}>
          sidebar
        </GridCol>
      </Grid>,
    );

    const section = container.querySelector("section");
    const article = container.querySelector("article");
    const aside = container.querySelector("aside");

    expect(section).toHaveClass("grid", "grid--justify-height");
    expect(article).toHaveClass("grid__col", "grid__col--8");
    expect(aside).toHaveClass("grid__col", "grid__col--4");
  });

  it("renders nested grid structure", () => {
    const { container } = render(
      <Grid>
        <GridCol size={6}>
          <Grid>
            <GridCol size={4}>nested 4</GridCol>
            <GridCol size={8}>nested 8</GridCol>
          </Grid>
        </GridCol>
        <GridCol size={6}>outer 6</GridCol>
      </Grid>,
    );

    const outerGrid = container.querySelector(".grid");
    const outerCols = outerGrid?.querySelectorAll(":scope > .grid__col");
    const nestedGrid = outerCols?.[0]?.querySelector(".grid");
    const nestedCols = nestedGrid?.querySelectorAll(":scope > .grid__col");

    // Filter out nested columns to get only direct children
    const directOuterCols = Array.from(outerCols || []).filter(
      (col) => col.parentElement === outerGrid,
    );

    expect(directOuterCols).toHaveLength(2);
    expect(directOuterCols[0]).toHaveClass("grid__col", "grid__col--6");
    expect(directOuterCols[1]).toHaveClass("grid__col", "grid__col--6");
    expect(nestedCols).toHaveLength(2);
    expect(nestedCols?.[0]).toHaveClass("grid__col", "grid__col--4");
    expect(nestedCols?.[1]).toHaveClass("grid__col", "grid__col--8");
  });

  it("renders grid with all size variations", () => {
    const { container } = render(
      <Grid>
        <GridCol size={1}>1</GridCol>
        <GridCol size={2}>2</GridCol>
        <GridCol size={3}>3</GridCol>
        <GridCol size={4}>4</GridCol>
        <GridCol size={5}>5</GridCol>
        <GridCol size={6}>6</GridCol>
        <GridCol size={7}>7</GridCol>
        <GridCol size={8}>8</GridCol>
        <GridCol size={9}>9</GridCol>
        <GridCol size={10}>10</GridCol>
        <GridCol size={11}>11</GridCol>
        <GridCol size={12}>12</GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    expect(cols).toHaveLength(12);

    for (let i = 0; i < 12; i++) {
      expect(cols[i]).toHaveClass("grid__col", `grid__col--${i + 1}`);
    }
  });

  it("renders grid with gap and custom styling", () => {
    const { container } = render(
      <Grid rowGapSize="large" columnGapSize="none" className="custom-grid">
        <GridCol size="auto" className="custom-col">
          auto
        </GridCol>
        <GridCol size="fill" className="custom-col">
          fill
        </GridCol>
        <GridCol size="shrink" className="custom-col">
          shrink
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];

    expect(grid).toHaveClass(
      "grid-row-gap--large",
      "grid-column-gap--none",
      "custom-grid",
    );
    expect(cols[0]).toHaveClass("grid__col", "grid__col--auto", "custom-col");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--fill", "custom-col");
    expect(cols[2]).toHaveClass("grid__col", "grid__col--shrink", "custom-col");
  });
});

describe("Grid and GridCol - Real-world scenarios", () => {
  it("renders scenario 1: default 12 + size 6", () => {
    const { container } = render(
      <Grid>
        <GridCol>
          <code>(default) 12</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    const codes = container.querySelectorAll("code");

    expect(cols).toHaveLength(2);
    expect(cols[0]).toHaveClass("grid__col");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--6");
    expect(codes[0]).toHaveTextContent("(default) 12");
    expect(codes[1]).toHaveTextContent("6");
  });

  it("renders scenario 2: auto + 4 + 6", () => {
    const { container } = render(
      <Grid>
        <GridCol size="auto">
          <code>auto</code>
        </GridCol>
        <GridCol size={4}>
          <code>5</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    const codes = container.querySelectorAll("code");

    expect(cols[0]).toHaveClass("grid__col", "grid__col--auto");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--4");
    expect(cols[2]).toHaveClass("grid__col", "grid__col--6");
    expect(codes[0]).toHaveTextContent("auto");
    expect(codes[1]).toHaveTextContent("5");
    expect(codes[2]).toHaveTextContent("6");
  });

  it("renders scenario 3: fill + 4 + 6", () => {
    const { container } = render(
      <Grid>
        <GridCol size="fill">
          <code>fill</code>
        </GridCol>
        <GridCol size={4}>
          <code>5</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    const codes = container.querySelectorAll("code");

    expect(cols[0]).toHaveClass("grid__col", "grid__col--fill");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--4");
    expect(cols[2]).toHaveClass("grid__col", "grid__col--6");
    expect(codes[0]).toHaveTextContent("fill");
    expect(codes[1]).toHaveTextContent("5");
    expect(codes[2]).toHaveTextContent("6");
  });

  it("renders scenario 4: shrink + 6", () => {
    const { container } = render(
      <Grid>
        <GridCol size="shrink">
          <code>shrink</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const grid = container.querySelector(".grid");
    expect(grid).toBeTruthy();
    const cols = grid ? grid.querySelectorAll(":scope > .grid__col") : [];
    const codes = container.querySelectorAll("code");

    expect(cols[0]).toHaveClass("grid__col", "grid__col--shrink");
    expect(cols[1]).toHaveClass("grid__col", "grid__col--6");
    expect(codes[0]).toHaveTextContent("shrink");
    expect(codes[1]).toHaveTextContent("6");
  });

  it("renders all scenarios together with hr separators", () => {
    const { container } = render(
      <>
        <Grid>
          <GridCol>
            <code>(default) 12</code>
          </GridCol>
          <GridCol size={6}>
            <code>6</code>
          </GridCol>
        </Grid>
        <hr />
        <Grid>
          <GridCol size="auto">
            <code>auto</code>
          </GridCol>
          <GridCol size={4}>
            <code>5</code>
          </GridCol>
          <GridCol size={6}>
            <code>6</code>
          </GridCol>
        </Grid>
        <hr />
        <Grid>
          <GridCol size="fill">
            <code>fill</code>
          </GridCol>
          <GridCol size={4}>
            <code>5</code>
          </GridCol>
          <GridCol size={6}>
            <code>6</code>
          </GridCol>
        </Grid>
        <hr />
        <Grid>
          <GridCol size="shrink">
            <code>shrink</code>
          </GridCol>
          <GridCol size={6}>
            <code>6</code>
          </GridCol>
        </Grid>
      </>,
    );

    const grids = container.querySelectorAll(".grid");
    const allCols = [];
    grids.forEach((grid) => {
      const cols = grid.querySelectorAll(":scope > .grid__col");
      cols.forEach((col) => allCols.push(col));
    });
    const codes = container.querySelectorAll("code");
    const hrs = container.querySelectorAll("hr");

    expect(grids).toHaveLength(4);
    expect(allCols).toHaveLength(10); // 2 + 3 + 3 + 2
    expect(codes).toHaveLength(10);
    expect(hrs).toHaveLength(3);
    // Prvý grid
    expect(allCols[0]).toHaveClass("grid__col");
    expect(allCols[1]).toHaveClass("grid__col", "grid__col--6");
    // Druhý grid
    expect(allCols[2]).toHaveClass("grid__col", "grid__col--auto");
    expect(allCols[3]).toHaveClass("grid__col", "grid__col--4");
    expect(allCols[4]).toHaveClass("grid__col", "grid__col--6");
    // Tretí grid
    expect(allCols[5]).toHaveClass("grid__col", "grid__col--fill");
    expect(allCols[6]).toHaveClass("grid__col", "grid__col--4");
    expect(allCols[7]).toHaveClass("grid__col", "grid__col--6");
    // Štvrtý grid
    expect(allCols[8]).toHaveClass("grid__col", "grid__col--shrink");
    expect(allCols[9]).toHaveClass("grid__col", "grid__col--6");
  });
});

describe("Grid and GridCol - HTML structure verification", () => {
  it("generates correct HTML structure for scenario 1", () => {
    const { container } = render(
      <Grid>
        <GridCol>
          <code>(default) 12</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    // Verify the generated HTML structure matches expected
    const html = container.innerHTML;
    expect(html).toContain('<div class="grid">');
    expect(html).toContain('<div class="grid__col">');
    expect(html).toContain('<div class="grid__col grid__col--6">');
    expect(html).toContain("<code>(default) 12</code>");
    expect(html).toContain("<code>6</code>");
  });

  it("generates correct HTML structure for scenario 2", () => {
    const { container } = render(
      <Grid>
        <GridCol size="auto">
          <code>auto</code>
        </GridCol>
        <GridCol size={4}>
          <code>5</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const html = container.innerHTML;
    expect(html).toContain('<div class="grid">');
    expect(html).toContain('<div class="grid__col grid__col--auto">');
    expect(html).toContain('<div class="grid__col grid__col--4">');
    expect(html).toContain('<div class="grid__col grid__col--6">');
    expect(html).toContain("<code>auto</code>");
    expect(html).toContain("<code>5</code>");
    expect(html).toContain("<code>6</code>");
  });

  it("generates correct HTML structure for scenario 3", () => {
    const { container } = render(
      <Grid>
        <GridCol size="fill">
          <code>fill</code>
        </GridCol>
        <GridCol size={4}>
          <code>5</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const html = container.innerHTML;
    expect(html).toContain('<div class="grid">');
    expect(html).toContain('<div class="grid__col grid__col--fill">');
    expect(html).toContain('<div class="grid__col grid__col--4">');
    expect(html).toContain('<div class="grid__col grid__col--6">');
    expect(html).toContain("<code>fill</code>");
    expect(html).toContain("<code>5</code>");
    expect(html).toContain("<code>6</code>");
  });

  it("generates correct HTML structure for scenario 4", () => {
    const { container } = render(
      <Grid>
        <GridCol size="shrink">
          <code>shrink</code>
        </GridCol>
        <GridCol size={6}>
          <code>6</code>
        </GridCol>
      </Grid>,
    );

    const html = container.innerHTML;
    expect(html).toContain('<div class="grid">');
    expect(html).toContain('<div class="grid__col grid__col--shrink">');
    expect(html).toContain('<div class="grid__col grid__col--6">');
    expect(html).toContain("<code>shrink</code>");
    expect(html).toContain("<code>6</code>");
  });
});
