import { createRef } from "react";
import { render } from "@testing-library/react";
import { Box } from ".";

const CLASS_NAME = "mobius-box";

describe("Box", () => {
  it("should render without errors", () => {
    render(<Box>Sample text</Box>);
  });

  describe("given elementType", () => {
    it("is not applied as element attribute", () => {
      const { container } = render(<Box elementType="span" />);

      expect(container.firstChild).not.toHaveAttribute("elementtype", "span");
    });
  });

  describe("uses correct class names", () => {
    it("uses mobius class", () => {
      const { container } = render(<Box>Sample text</Box>);

      expect(container.firstChild).toHaveClass("mobius");
    });

    it("uses base Mobius class", () => {
      const { container } = render(<Box>Sample text</Box>);

      expect(container.firstChild).toHaveClass(CLASS_NAME);
    });

    it("includes custom class name", () => {
      const customClassName = "my-class";
      const { container } = render(
        <Box className={customClassName}>Sample Text</Box>,
      );

      expect(container.firstChild).toHaveClass(customClassName);
      expect(container.firstChild).toHaveClass(CLASS_NAME);
    });
  });

  describe("given other props", () => {
    it("passes them to the element", () => {
      const { container } = render(
        <Box data-testid="test-id">Sample text</Box>,
      );

      expect(container.firstChild).toHaveAttribute("data-testid", "test-id");
    });
  });

  describe("given all padding props", () => {
    it("regardless of order, prioritises paddingTop/Right/Bottom/Left values over others", () => {
      const paddingTop = 100;
      const paddingRight = 110;
      const paddingBottom = 120;
      const paddingLeft = 130;
      const padding = 10;
      const paddingX = 20;
      const paddingInline = 30;
      const paddingInlineStart = 40;
      const paddingInlineEnd = 50;
      const paddingY = 60;
      const paddingBlock = 70;
      const paddingBlockStart = 80;
      const paddingBlockEnd = 90;

      const { container } = render(
        <Box
          paddingTop={paddingTop}
          paddingRight={paddingRight}
          paddingBottom={paddingBottom}
          paddingLeft={paddingLeft}
          padding={padding}
          paddingX={paddingX}
          paddingInline={paddingInline}
          paddingInlineStart={paddingInlineStart}
          paddingInlineEnd={paddingInlineEnd}
          paddingY={paddingY}
          paddingBlock={paddingBlock}
          paddingBlockStart={paddingBlockStart}
          paddingBlockEnd={paddingBlockEnd}
        />,
      );

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.paddingTop).toEqual(`${paddingTop}px`);
      expect(elementStyles.paddingRight).toEqual(`${paddingRight}px`);
      expect(elementStyles.paddingBottom).toEqual(`${paddingBottom}px`);
      expect(elementStyles.paddingLeft).toEqual(`${paddingLeft}px`);
    });
  });

  describe("given padding inline start/end, block start/end props", () => {
    it("regardless of order, prioritises inline/block start/end values over others", () => {
      const padding = 10;
      const paddingX = 20;
      const paddingInline = 30;
      const paddingInlineStart = 40;
      const paddingInlineEnd = 50;
      const paddingY = 60;
      const paddingBlock = 70;
      const paddingBlockStart = 80;
      const paddingBlockEnd = 90;

      const { container } = render(
        <Box
          paddingBlockStart={paddingBlockStart}
          paddingInlineEnd={paddingInlineEnd}
          paddingBlockEnd={paddingBlockEnd}
          paddingInlineStart={paddingInlineStart}
          padding={padding}
          paddingX={paddingX}
          paddingInline={paddingInline}
          paddingY={paddingY}
          paddingBlock={paddingBlock}
        />,
      );

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.paddingTop).toEqual(`${paddingBlockStart}px`);
      expect(elementStyles.paddingRight).toEqual(`${paddingInlineEnd}px`);
      expect(elementStyles.paddingBottom).toEqual(`${paddingBlockEnd}px`);
      expect(elementStyles.paddingLeft).toEqual(`${paddingInlineStart}px`);
    });
  });

  describe("given padding inline, block props", () => {
    it("regardless of order, prioritises inline/block values over x/y aliases", () => {
      // If `padding` value is provided, it does not reflect Chrome browser behaviour
      // JSDOM applies `padding` for all sides
      // const padding = 10;
      const paddingInline = 30;
      const paddingBlock = 70;
      const paddingX = 20;
      const paddingY = 60;

      const { container } = render(
        <Box
          // padding={padding}
          paddingX={paddingX}
          paddingInline={paddingInline}
          paddingY={paddingY}
          paddingBlock={paddingBlock}
        />,
      );

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.paddingBlock).toEqual(`${paddingBlock}px`);
      expect(elementStyles.paddingInline).toEqual(`${paddingInline}px`);
    });
  });

  describe("given padding X, Y props", () => {
    it("applies values as padding-inline, padding-block respectively ", () => {
      const paddingX = 20;
      const paddingY = 60;

      const { container } = render(
        <Box paddingX={paddingX} paddingY={paddingY} />,
      );

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.paddingBlock).toEqual(`${paddingY}px`);
      expect(elementStyles.paddingInline).toEqual(`${paddingX}px`);
    });
  });

  describe("given padding prop", () => {
    it("applies same padding value for all sides", () => {
      const padding = 10;

      const { container } = render(<Box padding={padding} />);

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.padding).toEqual(`${padding}px`);
    });
  });

  describe("given minWidth prop", () => {
    it.each([
      "minWidth",
      "minHeight",
      "maxWidth",
      "maxHeight",
      "width",
      "height",
    ])("applies %s value", (propName: string) => {
      const value = 100;

      const { container } = render(<Box {...{ [propName]: value }} />);

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles[propName as keyof CSSStyleDeclaration]).toEqual(
        `${value}px`,
      );
    });
  });

  describe("given style prop", () => {
    it("overrides default styles", () => {
      const { container } = render(
        <Box padding={50} style={{ padding: 100 }}>
          Sample text
        </Box>,
      );

      const elementStyles = (container.firstChild as HTMLElement)?.style;

      expect(elementStyles.padding).toEqual("100px");
    });

    it("does not remove prop padding when style padding is undefined", () => {
      const { container } = render(
        <Box padding={50} style={{ padding: undefined }} />,
      );

      expect(container.firstChild).toHaveStyle({ padding: "50px" });
    });

    it("does not remove prop padding when style padding is an empty string", () => {
      const { container } = render(
        <Box padding={50} style={{ padding: "" }} />,
      );

      expect(container.firstChild).toHaveStyle({ padding: "50px" });
    });
  });

  it("forwards ref", () => {
    const ref = createRef<HTMLDivElement>();
    render(<Box ref={ref}>Text</Box>);
    expect(ref.current).toBeInstanceOf(HTMLDivElement);
  });
});
