import { useMemo, type ReactNode } from "react";

import {
  documentToReactComponents,
  type Options,
} from "@contentful/rich-text-react-renderer";
import {
  BLOCKS,
  INLINES,
  MARKS,
  type Document,
} from "@contentful/rich-text-types";
import { Checklist } from "@shared/components/checklist";
import { Link } from "@shared/components/link";
import { MaterialIcon } from "@shared/components/material-icon";
import { Text } from "@shared/components/text";
import { toDocument } from "@shared/utils/contentful/to-document";

const isEmptyParagraph = (node: any): boolean =>
  Array.isArray(node?.content) &&
  node.content.every(
    (child: any) =>
      child?.nodeType === "text" && (!child.value || child.value.trim() === "")
  );

// A link is "external" only when it belongs to a different app than the one
// currently rendering this shared component (business vs. main/www are separate
// apps). Matching is environment-agnostic so a prod link like
// business.gokinetic.com is still "internal" while running on business-dev.
const hostFromUrl = (value?: string): string => {
  if (!value) return "";
  try {
    const withProtocol = /^https?:\/\//i.test(value)
      ? value
      : `https://${value}`;
    return new URL(withProtocol).hostname.toLowerCase();
  } catch {
    return "";
  }
};

// e.g. "business-dev.gokinetic.com" -> "gokinetic.com|business",
//      "kinetic-dev.gokinetic.com" / "www.gokinetic.com" -> "gokinetic.com|main"
const appIdentity = (host: string): string => {
  const clean = host.toLowerCase().replace(/\.$/, "");
  const base = clean.split(".").slice(-2).join(".");
  const subdomain = clean
    .slice(0, clean.length - base.length)
    .replace(/\.$/, "");
  const firstLabel = (subdomain.split(".")[0] || "").replace(
    /-(dev|development|staging|stage|qa|test|uat|prod|production)$/,
    ""
  );
  const app =
    firstLabel === "" || firstLabel === "www" || firstLabel === "kinetic"
      ? "main"
      : firstLabel;
  return `${base}|${app}`;
};

const getCurrentHost = (): string => {
  const envHost =
    hostFromUrl(process.env.NEXT_PUBLIC_SITE_URL) ||
    hostFromUrl(process.env.NEXT_PUBLIC_MAIN_SITE_URL);
  if (envHost) return envHost;
  if (typeof window !== "undefined")
    return window.location.hostname.toLowerCase();
  return "";
};

const isExternalUrl = (url: string): boolean => {
  if (!/^https?:\/\//i.test(url)) return false; // relative links are always internal
  try {
    const linkHost = new URL(url).hostname.toLowerCase();
    const currentHost = getCurrentHost();
    if (!currentHost) return true; // host unknown (SSR without env) → treat as external
    return appIdentity(linkHost) !== appIdentity(currentHost);
  } catch {
    return false;
  }
};

const defaultOptions: Options = {
  renderMark: {
    [MARKS.BOLD]: text => <strong className="font-black">{text}</strong>,
    [MARKS.ITALIC]: text => <em>{text}</em>,
    [MARKS.UNDERLINE]: text => <u>{text}</u>,
    [MARKS.CODE]: text => <code>{text}</code>,
  },
  renderNode: {
    [BLOCKS.PARAGRAPH]: (node, children) => (
      <div className="body3 mb-4 whitespace-pre-line">
        {isEmptyParagraph(node) ? "\n" : children}
      </div>
    ),
    [BLOCKS.HEADING_1]: (node, children) => {
      return (
        <Text as="h1" className={"heading2 md:heading1"}>
          {children}
        </Text>
      );
    },
    [BLOCKS.HEADING_2]: (node, children) => (
      <Text as="h2" className={"heading3 md:heading2"}>
        {children}
      </Text>
    ),
    [BLOCKS.HEADING_3]: (node, children) => (
      <Text as="h3" className={"heading4 md:heading3"}>
        {children}
      </Text>
    ),
    [BLOCKS.HEADING_4]: (node, children) => (
      <Text as="h4" className={"heading5 md:heading4"}>
        {children}
      </Text>
    ),
    [BLOCKS.HEADING_5]: (node, children) => (
      <Text as="h5" className={"heading6 md:heading5"}>
        {children}
      </Text>
    ),
    [BLOCKS.HEADING_6]: (node, children) => (
      <Text as="h6" className={"heading6"}>
        {children}
      </Text>
    ),
    [BLOCKS.QUOTE]: (_node, children) => <blockquote>{children}</blockquote>,
    [BLOCKS.UL_LIST]: (_node, children) => (
      <ul className="list-disc pl-6">{children}</ul>
    ),
    [BLOCKS.OL_LIST]: (_node, children) => (
      <ol className="list-decimal pl-6">{children}</ol>
    ),
    [BLOCKS.LIST_ITEM]: (_node, children) => <li>{children}</li>,

    [INLINES.HYPERLINK]: (node, children) => {
      const url = (node as any)?.data?.uri as string;
      const external = isExternalUrl(url);
      return (
        <a
          href={url}
          target={external ? "_blank" : undefined}
          rel={external ? "noopener" : undefined}
        >
          {children}
        </a>
      );
    },
    [BLOCKS.EMBEDDED_ASSET]: node => {
      const target = (node as any)?.data?.target;
      const fields = target?.fields;
      const url = target?.url || fields?.file?.url;
      const alt = fields?.title || fields?.description || "Embedded asset";
      if (!url) return null;
      const src = url.startsWith("//") ? `https:${url}` : url;
      return <img src={src} alt={alt} style={{ maxWidth: "100%" }} />;
    },

    [BLOCKS.EMBEDDED_ENTRY]: node => {
      const entry = (node as any)?.data?.target;
      const ctid = entry?.sys?.contentType?.sys?.id;
      switch (ctid) {
        case "callout":
          return (
            <aside
              style={{ border: "1px solid #ddd", padding: 12, borderRadius: 8 }}
            >
              <strong>{entry.fields.title}</strong>
              <div>{entry.fields.body}</div>
            </aside>
          );
        default:
          return null;
      }
    },

    [INLINES.EMBEDDED_ENTRY]: node => {
      const entry = (node as any)?.data?.target;
      const ctid = entry?.sys?.contentType?.sys?.id;
      switch (ctid) {
        case "componentCheckList":
          return (
            <span
              style={{
                display: "inline-flex",
                alignItems: "center",
                gap: "4px",
                padding: "2px 6px",
                backgroundColor: "#f0fdf4",
                border: "1px solid #bbf7d0",
                borderRadius: "4px",
                fontSize: "0.9em",
              }}
            >
              ✅ {entry.fields.title}
            </span>
          );
        default:
          return <span>{entry?.fields?.title ?? ""}</span>;
      }
    },
  },
};
/**
 * Utility function to render Contentful Rich Text.
 * Safe to use inside .map() loops.
 */
export function renderContentfulRichText(
  doc: Document | null | undefined,
  target?: boolean,
  className: string = "body1",
  linkClassName: string = "body1 font-bold",
  options?: Options
): ReactNode | null {
  if (!doc || !Array.isArray(doc.content)) return null;

  const merged: Options = {
    ...defaultOptions,
    ...options,
    renderNode: {
      ...defaultOptions.renderNode,
      ...options?.renderNode,
      // Logic for links based on the isTargetBlank flag
      [BLOCKS.PARAGRAPH]: (node, children) => (
        <div
          className={[className, "whitespace-pre-line"]
            .filter(Boolean)
            .join(" ")}
        >
          {isEmptyParagraph(node) ? "\n" : children}
        </div>
      ),
      [INLINES.HYPERLINK]: (node, children) => {
        const url = (node as any)?.data?.uri as string;
        const external = isExternalUrl(url);

        // Priority: 1. Manual flag from Contentful, 2. Same-domain URLs are internal
        const shouldOpenBlank = target ?? external;

        return (
          <Link
            href={url}
            target={shouldOpenBlank ? "_blank" : undefined}
            rel={shouldOpenBlank ? "noopener" : undefined}
            variant="default"
            className={linkClassName}
          >
            {children}
          </Link>
        );
      },
    },
  };

  // Directly return the rendered components without useMemo
  return documentToReactComponents(doc, merged);
}

export function useContentfulRichText(
  doc: Document | null | undefined,
  options?: Options,
  config?: { boldClassName?: string }
): ReactNode | null {
  return useMemo(() => {
    if (!doc || !Array.isArray(doc.content)) return null;
    const merged: Options = {
      ...defaultOptions,
      renderMark: {
        ...defaultOptions.renderMark,
        ...(config?.boldClassName
          ? {
              [MARKS.BOLD]: text => (
                <strong className={config.boldClassName}>{text}</strong>
              ),
            }
          : {}),
        ...options?.renderMark,
      },
      renderNode: { ...defaultOptions.renderNode, ...options?.renderNode },
    };
    return documentToReactComponents(doc, merged);
  }, [doc, options, config?.boldClassName]);
}
// 1. Remove useMemo and React Hook imports from this specific function's logic
export function renderContentfulRichTextTable(
  doc: Document | null | undefined,
  links?: any
) {
  if (!doc || !Array.isArray(doc.content)) return null;

  const options: Options = {
    ...defaultOptions, // Spread defaultOptions so <p> and <strong> still work!
    renderMark: {
      ...defaultOptions.renderMark,
      [MARKS.BOLD]: text => (
        <strong className="label4 md:label2">{text}</strong>
      ),
    },
    renderNode: {
      ...defaultOptions.renderNode,
      [BLOCKS.PARAGRAPH]: (_node, children) => <>{children}</>,
      [BLOCKS.TABLE]: (node: any, children) => (
        <div className="comparison-table-wrapper w-full overflow-x-auto border-none md:overflow-hidden">
          <table
            className={`responsive-table w-full table-fixed border-collapse ${
              node.content[0]?.content?.length > 2
                ? "min-w-[100.1%]"
                : "min-w-full"
            }`}
          >
            <tbody>{children}</tbody>
          </table>
        </div>
      ),
      [BLOCKS.TABLE_ROW]: (node, children) => (
        <tr className="border-b border-gray-200 last:border-0">{children}</tr>
      ),
      [BLOCKS.TABLE_HEADER_CELL]: (node: any, children) => {
        const isScrollable = node.parent?.content.length > 2;
        return (
          <th
            className={`label4 break-words py-4 text-center md:label2 ${isScrollable ? "sticky left-0 z-20 w-[50vw] min-w-[50vw] first:z-30 first:w-[50vw] first:min-w-[50vw] first:border-r" : "w-1/4 first:w-1/2"} `}
          >
            {children}
          </th>
        );
      },
      [BLOCKS.TABLE_CELL]: (node: any, children) => {
        const isScrollable = node.parent?.content.length > 2;

        // Logic to check for "yes" or "no"
        // children[0].props.children is usually where the text sits if it's in a <p> tag
        const rawText = node.content[0]?.content[0]?.value
          ?.toLowerCase()
          .trim();

        const renderContent = () => {
          if (rawText === "yes") {
            return (
              <MaterialIcon name="check_circle" color="#24A76A" fill={1} />
            );
          }
          if (rawText === "no") {
            return <MaterialIcon name="cancel" color="#CECECE" fill={1} />;
          }
          return children;
        };

        return (
          <td
            className={`rt-table-cell footnote break-words bg-white py-2 text-center align-top leading-5 text-text md:body2 first:text-left md:py-4 md:leading-7 ${
              isScrollable
                ? "w-[50vw] min-w-[50vw] first:sticky first:left-0 first:z-10 first:w-[50vw] first:min-w-[50vw] first:border-r"
                : "w-1/4 first:w-1/2"
            } `}
          >
            <> {renderContent()}</>
          </td>
        );
      },
      [INLINES.EMBEDDED_ENTRY]: node => {
        const entryId = node.data.target.sys.id;

        const entry = links?.entries?.inline?.find(
          (e: any) => e.sys.id === entryId
        );
        if (!entry) return null;

        if (entry.__typename === "ComponentCheckList") {
          // FIX: Don't use a Hook here.
          // Map the items manually or use a non-hook helper function.
          const items =
            entry.list?.items?.map((item: any) =>
              renderContentfulRichText(
                toDocument(item?.checkListTitle ?? ""),
                true,
                ""
              )
            ) || [];
          return (
            <Checklist
              items={items}
              listIconName="disc"
              listItemClassName="items-baseline footnote md:body2 leading-5 md:leading-7 text-text"
            />
          );
        }

        return <span>{entry.title || ""}</span>;
      },
    },
  };

  return documentToReactComponents(doc, options);
}
