import { describe, it, expect, vi, beforeEach } from "vitest";
import { screen, waitFor } from "@testing-library/react";
import { renderWithProviders as render } from "../utils/test-utils";
import React from "react";
import BlogPostPage from "../../app/(marketing)/blog/[slug]/page";

// Mock Next.js components
vi.mock("next/navigation", () => ({
  notFound: vi.fn(),
}));

vi.mock("next/link", () => {
  return {
    default: ({ children, href, ...props }) => (
      <a href={href} {...props}>
        {children}
      </a>
    ),
  };
});

// Mock next/dynamic to return components synchronously in tests
vi.mock("next/dynamic", () => {
  return {
    default: (importFn, options) => {
      // In tests, resolve the dynamic import immediately and return the component
      let Component = null;
      importFn().then((mod) => {
        Component = mod.default || mod;
      });
      // Return a synchronous wrapper that uses the mocked component
      return (props) => {
        // Use the mocked RelatedArticles component directly
        if (Component) {
          return <Component {...props} />;
        }
        // Fallback: return the loading placeholder if component not ready
        return options?.loading ? options.loading() : null;
      };
    },
  };
});

// Mock content processing
vi.mock("../../lib/content", () => ({
  getBlogPostBySlug: vi.fn(),
  getAllBlogPosts: vi.fn(),
  getRelatedBlogPosts: vi.fn(() => []),
}));

// Mock components
vi.mock("../../app/components/sections/ContentBanner", () => {
  return {
    default: ({ post }) => (
      <div data-testid="content-banner">
        <h1>{post.frontmatter.title}</h1>
        <p>{post.frontmatter.description}</p>
      </div>
    ),
  };
});

vi.mock("../../app/components/sections/RelatedArticles", () => {
  return {
    default: ({ relatedPosts }) => (
      <div data-testid="related-articles">
        <h2>Related Articles</h2>
        {relatedPosts.map((post) => (
          <div key={post.slug} data-testid={`related-${post.slug}`}>
            {post.frontmatter.title}
          </div>
        ))}
      </div>
    ),
  };
});

vi.mock("../../app/components/sections/AskOrganizer", () => {
  return {
    default: ({ title, subtitle, buttonText }) => (
      <div data-testid="ask-organizer">
        <h2>{title}</h2>
        <p>{subtitle}</p>
        <button>{buttonText}</button>
      </div>
    ),
  };
});

// Mock asset utils
vi.mock("../../lib/assetUtils", async (importOriginal) => {
  const actual = await importOriginal();
  return {
    ...actual,
    getAssetPath: vi.fn((asset) =>
      asset.startsWith("/") ? asset : `/${asset}`,
    ),
  };
});

// Mock blog post data
const mockPost = {
  slug: "test-article",
  frontmatter: {
    title: "Test Article Title",
    description: "This is a test article description",
    author: "Test Author",
    date: "2025-04-15",
  },
  htmlContent:
    "<p>This is the article content with <strong>bold text</strong> and <em>italic text</em>.</p>",
};

const mockRelatedPosts = [
  {
    slug: "related-1",
    frontmatter: {
      title: "Related Article 1",
      description: "First related article",
      author: "Test Author",
      date: "2025-04-10",
    },
  },
  {
    slug: "related-2",
    frontmatter: {
      title: "Related Article 2",
      description: "Second related article",
      author: "Test Author",
      date: "2025-04-12",
    },
  },
];

describe("BlogPostPage", () => {
  beforeEach(async () => {
    // Reset mocks
    vi.clearAllMocks();

    // Mock the content functions
    const { getBlogPostBySlug, getAllBlogPosts, getRelatedBlogPosts } =
      await import("../../lib/content");
    vi.mocked(getBlogPostBySlug).mockReturnValue(mockPost);
    vi.mocked(getAllBlogPosts).mockReturnValue([mockPost, ...mockRelatedPosts]);
    vi.mocked(getRelatedBlogPosts).mockReturnValue(mockRelatedPosts);
  });

  it("renders the blog post page with correct structure", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    // Check main container (it's a div, not main)
    const mainContainer = document.querySelector("div.min-h-screen");
    expect(mainContainer).toBeInTheDocument();
    expect(mainContainer).toHaveClass(
      "min-h-screen",
      "relative",
      "overflow-x-clip",
    );
    // Background color is applied via inline style from frontmatter hex
    expect(mainContainer).toHaveStyle({ backgroundColor: expect.any(String) });
  });

  it("renders the content banner", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    expect(screen.getByTestId("content-banner")).toBeInTheDocument();
    expect(screen.getByText("Test Article Title")).toBeInTheDocument();
    expect(
      screen.getByText("This is a test article description"),
    ).toBeInTheDocument();
  });

  it("renders the article content", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    const article = document.querySelector("article");
    expect(article).toBeInTheDocument();
    expect(article).toHaveClass(
      "p-[var(--spacing-scale-024)]",
      "sm:py-[var(--spacing-scale-032)]",
    );

    // Check content is rendered
    expect(screen.getByText(/This is the article content/)).toBeInTheDocument();
    expect(screen.getByText("bold text")).toBeInTheDocument();
    expect(screen.getByText("italic text")).toBeInTheDocument();
  });

  it("renders the related articles section", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    // Wait for dynamically imported RelatedArticles component to load
    await waitFor(() => {
      expect(screen.getByTestId("related-articles")).toBeInTheDocument();
    });

    expect(screen.getByText("Related Articles")).toBeInTheDocument();
    expect(screen.getByTestId("related-related-1")).toBeInTheDocument();
    expect(screen.getByTestId("related-related-2")).toBeInTheDocument();
  });

  it("renders the ask organizer section", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    expect(screen.getByTestId("ask-organizer")).toBeInTheDocument();
    expect(screen.getByText("Still have questions?")).toBeInTheDocument();
    expect(
      screen.getByText("Get answers from an experienced organizer"),
    ).toBeInTheDocument();
    expect(screen.getByText("Ask an organizer")).toBeInTheDocument();
  });

  it("renders decorative shapes", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    // Check for decorative shapes
    const shapes = screen.getAllByAltText("");
    expect(shapes).toHaveLength(2);

    // Check shape sources
    expect(shapes[0]).toHaveAttribute("src", "/assets/shapes/content-shape-1.svg");
    expect(shapes[1]).toHaveAttribute("src", "/assets/shapes/content-shape-2.svg");
    expect(shapes[0]).toHaveClass("object-contain");
    expect(shapes[1]).toHaveClass("object-contain");
  });

  it("uses per-article body ornaments for catalog posts", async () => {
    const { getBlogPostBySlug } = await import("../../lib/content");
    vi.mocked(getBlogPostBySlug).mockReturnValue({
      ...mockPost,
      slug: "resolving-active-conflicts",
    });

    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "resolving-active-conflicts" },
    });
    render(BlogPostPageComponent);

    const shapes = screen.getAllByAltText("");
    expect(shapes[0]).toHaveAttribute(
      "src",
      "/content/blog/resolving-active-conflicts-ornament-right.png",
    );
    expect(shapes[1]).toHaveAttribute(
      "src",
      "/content/blog/resolving-active-conflicts-ornament-left.png",
    );
  });

  it("applies correct styling to article content", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    const contentDiv = screen
      .getByText(/This is the article content/)
      .closest("div.post-body");
    expect(contentDiv).toHaveClass("post-body");
    expect(contentDiv).toHaveClass(
      "text-[var(--color-content-inverse-primary)]",
    );
    expect(contentDiv).toHaveClass("text-medium-paragraph");

    const article = contentDiv?.closest("article");
    expect(article).toHaveClass("p-[var(--spacing-scale-024)]");
    expect(article).toHaveClass("sm:py-[var(--spacing-scale-032)]");
    expect(article).not.toHaveClass("-mt-[var(--spacing-scale-048)]");
  });

  it("applies responsive text sizing", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    const contentDiv = screen
      .getByText(/This is the article content/)
      .closest("div.post-body");
    expect(contentDiv).toHaveClass("sm:text-large-paragraph");
    expect(contentDiv).toHaveClass("lg:text-x-large-paragraph");
    expect(contentDiv).toHaveClass("xl:text-xx-large-paragraph");
  });

  it("applies responsive max-width constraints", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    const contentDiv = screen
      .getByText(/This is the article content/)
      .closest("div.post-body");
    const article = contentDiv?.closest("article");
    expect(contentDiv).toHaveClass("sm:max-w-[390px]");
    expect(contentDiv).toHaveClass("md:max-w-[472px]");
    expect(contentDiv).toHaveClass("lg:max-w-[700px]");
    expect(contentDiv).toHaveClass("xl:max-w-none");
    expect(article).toHaveClass("xl:px-[calc(268/1440*100%)]");
  });

  it("includes structured data scripts", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    // Check for script elements using querySelector since RTL ignores them
    const scripts = document.querySelectorAll(
      'script[type="application/ld+json"]',
    );
    expect(scripts).toHaveLength(2);

    // Check that scripts have the correct type and content
    scripts.forEach((script) => {
      expect(script).toHaveAttribute("type", "application/ld+json");
      expect(script.innerHTML).toBeTruthy();
    });
  });

  it("handles missing post gracefully", async () => {
    const { getBlogPostBySlug } = await import("../../lib/content");
    vi.mocked(getBlogPostBySlug).mockReturnValue(null);

    // The component should throw an error when post is null
    // This happens because notFound() is called
    await expect(
      BlogPostPage({ params: { slug: "non-existent" } }),
    ).rejects.toThrow();
  });

  it("filters out current post from related articles", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    // Wait for dynamically imported RelatedArticles component to load
    await waitFor(() => {
      expect(screen.getByTestId("related-articles")).toBeInTheDocument();
    });

    // Current post should not appear in related articles
    expect(
      screen.queryByTestId("related-test-article"),
    ).not.toBeInTheDocument();

    // Other related posts should appear
    expect(screen.getByTestId("related-related-1")).toBeInTheDocument();
    expect(screen.getByTestId("related-related-2")).toBeInTheDocument();
  });

  it("applies correct positioning to decorative shapes", async () => {
    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "test-article" },
    });
    render(BlogPostPageComponent);

    const shapes = screen.getAllByAltText("");

    const rightShape = shapes[0].closest("div");
    expect(rightShape).toHaveClass(
      "hidden",
      "md:block",
      "absolute",
      "pointer-events-none",
      "z-0",
      "top-[266px]",
      "lg:top-[255px]",
      "left-[87.19%]",
      "lg:left-[84.86%]",
    );
    expect(rightShape).not.toHaveClass("z-10");
    expect(rightShape).not.toHaveClass("top-1/4");

    const leftShape = shapes[1].closest("div");
    expect(leftShape).toHaveClass(
      "hidden",
      "md:block",
      "absolute",
      "pointer-events-none",
      "z-0",
      "top-[811px]",
      "left-[-1.66%]",
    );
    expect(leftShape).not.toHaveClass("z-10");
    expect(leftShape).not.toHaveClass("top-1/2");
  });

  it("handles malformed post data gracefully", async () => {
    const malformedPost = {
      slug: "malformed",
      frontmatter: {
        title: "Malformed Post",
        description: "A malformed post for testing",
        author: "Test Author",
        date: "2025-01-15",
      },
      htmlContent: "<p>Content</p>",
    };

    const { getBlogPostBySlug } = await import("../../lib/content");
    vi.mocked(getBlogPostBySlug).mockReturnValue(malformedPost);

    const BlogPostPageComponent = await BlogPostPage({
      params: { slug: "malformed" },
    });
    render(BlogPostPageComponent);

    expect(screen.getByText("Malformed Post")).toBeInTheDocument();
    expect(screen.getByText("Content")).toBeInTheDocument();
  });
});
