import "@testing-library/jest-dom";

import React from "react";
import { Callout } from "./index";
import { CalloutProps } from "./types";

import { render, screen } from "@testing-library/react";

// Mock child card components
jest.mock("../cards/simple-card", () => ({
  __esModule: true,
  default: ({ card }: any) => (
    <div data-testid="simple-card">{card?.title || "Simple Card"}</div>
  ),
}));

jest.mock("../cards/blog-card", () => ({
  __esModule: true,
  default: ({ title }: any) => (
    <div data-testid="blog-card">{title || "Blog Card"}</div>
  ),
}));

jest.mock("../cards/full-image-card", () => ({
  __esModule: true,
  default: ({ card }: any) => (
    <div data-testid="full-image-card">{card?.title || "Full Image Card"}</div>
  ),
}));

jest.mock("../cards/floating-image-card", () => ({
  __esModule: true,
  default: ({ card }: any) => (
    <div data-testid="floating-image-card">
      {card?.title || "Floating Image Card"}
    </div>
  ),
}));

jest.mock("../button", () => ({
  Button: (props: any) => (
    <button data-testid="callout-button">
      {props.label ?? props.buttonLabel ?? props.children}
    </button>
  ),
}));

jest.mock("@shared/components/animation-wrapper", () => ({
  AnimationWrapper: ({ children }: any) => <>{children}</>,
}));

jest.mock("@shared/components/text", () => ({
  Text: ({ children, as, ...props }: any) => {
    const Tag = as || "span";
    return <Tag {...props}>{children}</Tag>;
  },
}));

jest.mock("@shared/utils", () => ({
  cx: (...args: any[]) => args.filter(Boolean).join(" "),
}));

describe("Callout", () => {
  const defaultProps: CalloutProps = {
    items: [
      { title: "Card 1", description: "Desc 1" },
      { title: "Card 2", description: "Desc 2" },
    ],
  };

  it("renders the section element", () => {
    const { container } = render(<Callout {...defaultProps} />);
    const section = container.querySelector("section");
    expect(section).toBeInTheDocument();
  });

  it("renders with anchorId", () => {
    const { container } = render(
      <Callout {...defaultProps} anchorId="test-anchor" />
    );
    const section = container.querySelector("section#test-anchor");
    expect(section).toBeInTheDocument();
  });

  it("renders title as h2 by default", () => {
    render(<Callout {...defaultProps} title="Test Title" />);
    const heading = screen.getByText("Test Title");
    expect(heading.tagName).toBe("H2");
  });

  it("renders title as h1 when enableHeading is true", () => {
    render(
      <Callout {...defaultProps} title="Main Title" enableHeading={true} />
    );
    const heading = screen.getByText("Main Title");
    expect(heading.tagName).toBe("H1");
  });

  it("renders subtitle", () => {
    render(<Callout {...defaultProps} subtitle="Test Subtitle" />);
    expect(screen.getByText("Test Subtitle")).toBeInTheDocument();
  });

  it("renders subtitle as h2 when enableHeading is true", () => {
    render(
      <Callout
        {...defaultProps}
        subtitle="Sub Title"
        enableHeading={true}
        title="X"
      />
    );
    const subtitle = screen.getByText("Sub Title");
    expect(subtitle.tagName).toBe("H2");
  });

  it("renders subtitle as h3 by default", () => {
    render(<Callout {...defaultProps} subtitle="Sub H3" title="X" />);
    const subtitle = screen.getByText("Sub H3");
    expect(subtitle.tagName).toBe("H3");
  });

  it("renders description", () => {
    render(<Callout {...defaultProps} description="A description" />);
    expect(screen.getByText("A description")).toBeInTheDocument();
  });

  it("renders finePrint", () => {
    render(<Callout {...defaultProps} finePrint="Fine print text" />);
    expect(screen.getByText("Fine print text")).toBeInTheDocument();
  });

  it("renders CTA button with label", () => {
    render(
      <Callout {...defaultProps} cta={{ label: "Click me", href: "/page" }} />
    );
    expect(screen.getByTestId("callout-button")).toHaveTextContent("Click me");
  });

  it("renders CTA button with buttonLabel fallback", () => {
    render(
      <Callout
        {...defaultProps}
        cta={{ buttonLabel: "ButtonLabel", href: "/page" }}
      />
    );
    expect(screen.getByTestId("callout-button")).toHaveTextContent(
      "ButtonLabel"
    );
  });

  it("does not render CTA section when no cta and no finePrint", () => {
    const { container } = render(<Callout {...defaultProps} />);
    expect(
      container.querySelector("[data-testid='callout-button']")
    ).toBeNull();
  });

  describe("flex layout", () => {
    it("uses flex layout by default", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.className).toContain("flex");
      expect(gridHolder?.className).toContain("flex-wrap");
    });

    it("applies per-card width classes based on column calculations", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const cards = container.querySelectorAll(".callout-card");
      expect(cards.length).toBe(2);
      // 2 items, cardStackingMobile=true => md cols = 1 => md:w-full
      cards.forEach(card => {
        expect(card.className).toContain("md:w-full");
        expect(card.className).toContain("lg:w-[calc(50%-0.75rem)]");
      });
    });

    it("uses flex-col layout when cardsWidth is false (stack mode)", () => {
      const { container } = render(
        <Callout {...defaultProps} cardsWidth={false} />
      );
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.className).toContain("flex");
      expect(gridHolder?.className).toContain("flex-col");
    });

    it("renders cards without wrapper div in stack mode", () => {
      const { container } = render(
        <Callout {...defaultProps} cardsWidth={false} />
      );
      // In stack mode, cards are not wrapped in .callout-card divs
      const cards = container.querySelectorAll(".callout-card");
      expect(cards.length).toBe(0);
      // But simple-card testids should still be present
      expect(screen.getAllByTestId("simple-card")).toHaveLength(2);
    });

    it("renders 1 item with full width on lg", () => {
      const { container } = render(
        <Callout items={[{ title: "Only card" }]} />
      );
      const card = container.querySelector(".callout-card");
      expect(card?.className).toContain("lg:w-full");
    });

    it("respects maxCardsPerRow override", () => {
      const items = Array.from({ length: 6 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(
        <Callout items={items} maxCardsPerRow={2} />
      );
      const cards = container.querySelectorAll(".callout-card");
      // maxCardsPerRow=2, lgCols = min(2, 6) = 2
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(50%-0.75rem)]");
      });
    });

    it("clamps maxCardsPerRow to max 6", () => {
      const items = Array.from({ length: 3 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(
        <Callout items={items} maxCardsPerRow={10} />
      );
      const cards = container.querySelectorAll(".callout-card");
      // clamp(10) = 6, lgCols = min(6, 3) = 3
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(33.333%-1rem)]");
      });
    });

    it("uses 2 columns on md when cardStackingMobile is false", () => {
      const { container } = render(
        <Callout {...defaultProps} cardStackingMobile={false} />
      );
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("md:w-[calc(50%-1rem)]");
      });
    });

    it("uses 1 column on md when cardStackingMobile is true (default)", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("md:w-full");
      });
    });

    it("removes gutter with noGutter prop", () => {
      const { container } = render(
        <Callout {...defaultProps} noGutter={true} />
      );
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.className).toContain("gap-0");
      expect(gridHolder?.className).not.toContain("gap-6");
    });

    it("has gap-6 by default", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.className).toContain("gap-6");
    });

    it("calculates 3 columns for 6 items (divisible by 3)", () => {
      const items = Array.from({ length: 6 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(<Callout items={items} />);
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(33.333%-1rem)]");
      });
    });

    it("calculates 4 columns for 8 items (divisible by 4)", () => {
      const items = Array.from({ length: 8 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(<Callout items={items} />);
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(25%-1.125rem)]");
      });
    });

    it("calculates 4 columns for 7 items (>6, not div by 3)", () => {
      const items = Array.from({ length: 7 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(<Callout items={items} />);
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(25%-1.125rem)]");
      });
    });

    it("calculates 3 columns for 5 items (fallback)", () => {
      const items = Array.from({ length: 5 }, (_, i) => ({
        title: `Item ${i}`,
      }));
      const { container } = render(<Callout items={items} />);
      const cards = container.querySelectorAll(".callout-card");
      cards.forEach(card => {
        expect(card.className).toContain("lg:w-[calc(33.333%-1rem)]");
      });
    });
  });

  describe("background colors", () => {
    it("applies cream500 background class", () => {
      const { container } = render(
        <Callout {...defaultProps} backgroundColor="cream500" />
      );
      const section = container.querySelector("section");
      expect(section?.className).toContain("bg-[#FFFEEF]");
    });

    it("applies gray100 background class", () => {
      const { container } = render(
        <Callout {...defaultProps} backgroundColor="gray100" />
      );
      const section = container.querySelector("section");
      expect(section?.className).toContain("bg-fill-secondary");
    });

    it("applies navy background class", () => {
      const { container } = render(
        <Callout {...defaultProps} backgroundColor="navy" />
      );
      const section = container.querySelector("section");
      expect(section?.className).toContain("bg-fill-inverse");
    });

    it("applies transparent background (no additional class)", () => {
      const { container } = render(
        <Callout {...defaultProps} backgroundColor="transparent" />
      );
      const section = container.querySelector("section");
      expect(section?.className).not.toContain("bg-fill");
    });

    it("uses inline style when background prop is provided", () => {
      const { container } = render(
        <Callout {...defaultProps} background="linear-gradient(red, blue)" />
      );
      const section = container.querySelector("section");
      expect(section).toHaveStyle({
        background: "linear-gradient(red, blue)",
      });
    });

    it("background prop overrides backgroundColor class", () => {
      const { container } = render(
        <Callout
          {...defaultProps}
          background="red"
          backgroundColor="cream500"
        />
      );
      const section = container.querySelector("section");
      expect(section?.className).not.toContain("bg-[#FFFEEF]");
      expect(section?.style.background).toBe("red");
    });
  });

  describe("text color", () => {
    it("applies dark color class by default", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).toContain("text-text");
    });

    it("applies light color class", () => {
      const { container } = render(<Callout {...defaultProps} color="light" />);
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).toContain("text-white");
    });

    it("applies textColor as inline style on title holder", () => {
      const { container } = render(
        <Callout {...defaultProps} title="Hi" textColor="#ff0000" />
      );
      const titleHolder = container.querySelector(".title-holder");
      expect(titleHolder).not.toBeNull();
      expect(titleHolder!.getAttribute("style")).toContain("color");
    });
  });

  describe("card types", () => {
    it("renders simple cards by default", () => {
      render(<Callout {...defaultProps} />);
      const cards = screen.getAllByTestId("simple-card");
      expect(cards).toHaveLength(2);
    });

    it("renders blog cards when cardType is blog", () => {
      const items = [
        {
          title: "Blog 1",
          slug: "/blog-1",
          shortDescription: "Desc",
          blogCreationDate: "2024-01-01",
          category: "Tech",
        },
      ];
      render(<Callout items={items} cardType="blog" />);
      expect(screen.getByTestId("blog-card")).toBeInTheDocument();
    });

    it("renders fullImage cards when cardType is fullImage", () => {
      render(<Callout {...defaultProps} cardType="fullImage" />);
      expect(screen.getAllByTestId("full-image-card")).toHaveLength(2);
    });

    it("renders floatingImage cards when cardType is floatingImage", () => {
      render(<Callout {...defaultProps} cardType="floatingImage" />);
      expect(screen.getAllByTestId("floating-image-card")).toHaveLength(2);
    });

    it("allows per-item cardType override", () => {
      const items = [
        { title: "Simple", cardType: "simple" as const },
        { title: "Blog", cardType: "blog" as const, slug: "/b" },
      ];
      render(<Callout items={items} />);
      expect(screen.getByTestId("simple-card")).toBeInTheDocument();
      expect(screen.getByTestId("blog-card")).toBeInTheDocument();
    });

    it("passes applyBoxShadow to card shadow prop", () => {
      const { container } = render(
        <Callout {...defaultProps} applyBoxShadow={true} />
      );
      expect(
        container.querySelectorAll("[data-testid='simple-card']")
      ).toHaveLength(2);
    });
  });

  describe("maxWidth and layout", () => {
    it("applies max-w-120 when maxWidth is true (default)", () => {
      const { container } = render(<Callout {...defaultProps} />);
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).toContain("max-w-120");
    });

    it("does not apply max-w-120 when maxWidth is false", () => {
      const { container } = render(
        <Callout {...defaultProps} maxWidth={false} />
      );
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).not.toContain("max-w-120");
    });

    it("applies containerClassName to section", () => {
      const { container } = render(
        <Callout {...defaultProps} containerClassName="custom-section" />
      );
      const section = container.querySelector("section");
      expect(section?.className).toContain("custom-section");
    });

    it("applies innerClassName to inner div", () => {
      const { container } = render(
        <Callout {...defaultProps} innerClassName="custom-inner" />
      );
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).toContain("custom-inner");
    });

    it("applies noGutter padding class", () => {
      const { container } = render(
        <Callout {...defaultProps} noGutter={true} />
      );
      const inner =
        container.querySelector(".callout-container")?.parentElement;
      expect(inner?.className).toContain("p-0");
    });
  });

  describe("edge cases", () => {
    it("handles empty items array", () => {
      const { container } = render(<Callout items={[]} />);
      expect(container.querySelector("section")).toBeInTheDocument();
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.children).toHaveLength(0);
    });

    it("handles items with no properties gracefully", () => {
      const { container } = render(<Callout items={[{}, {}]} />);
      expect(container.querySelector("section")).toBeInTheDocument();
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.children).toHaveLength(2);
    });

    it("handles unknown backgroundColor value with fallback", () => {
      const { container } = render(
        <Callout {...defaultProps} backgroundColor={"unknownColor" as any} />
      );
      const section = container.querySelector("section");
      expect(section).toBeInTheDocument();
    });

    it("renders with cardsWidth false and noGutter true", () => {
      const { container } = render(
        <Callout {...defaultProps} cardsWidth={false} noGutter={true} />
      );
      const gridHolder = container.querySelector(".card-holder");
      expect(gridHolder?.className).toContain("gap-0");
    });

    it("renders card with shadow prop already set on item", () => {
      const items = [{ title: "Card", shadow: true }];
      const { container } = render(
        <Callout items={items} applyBoxShadow={false} />
      );
      expect(
        container.querySelector("[data-testid='simple-card']")
      ).toBeInTheDocument();
    });

    it("calculates lgCols clamping to itemCount", () => {
      // When itemCount (1) < desktopCols (4), lgCols = itemCount = 1
      const { container } = render(
        <Callout items={[{ title: "Solo" }]} maxCardsPerRow={4} />
      );
      const card = container.querySelector(".callout-card");
      expect(card?.className).toContain("lg:w-full");
    });
  });
});
