import { createElement } from "react";
import { createRoot, type Root } from "react-dom/client";
import { DASHBOARD_TOGGLES, EDITOR_NOTE, EMBEDS, sanitizeDashboardToggles, sanitizeEmbedLimit, sanitizeEmbedRange, sanitizeEmbedTheme, sanitizeEmbedTitle, sanitizeEmbedWidth, type DashboardToggles, type EmbedAttributes, type EmbedKind } from "./embed-config";
import { EmbedView, ensureEmbedStyles, interpretEmbedResponse, type EmbedModel } from "./embed-view";

type WpElement = {
  createElement: (type: unknown, props?: unknown, ...children: unknown[]) => unknown;
  Fragment: unknown;
  useEffect: (effect: () => void | (() => void), deps?: unknown[]) => void;
  useRef: <T>(value: T) => { current: T };
};

type EditProps = {
  attributes: Partial<EmbedAttributes & DashboardToggles & { align: string; width: string; instance: string }>;
  setAttributes: (next: Partial<EmbedAttributes & DashboardToggles & { align: string; width: string; instance: string }>) => void;
};

type WpApi = {
  blocks?: { registerBlockType: (name: string, settings: Record<string, unknown>) => void };
  element?: WpElement;
  blockEditor?: {
    InspectorControls: unknown;
    useBlockProps: (props?: Record<string, unknown>) => Record<string, unknown>;
  };
  components?: {
    PanelBody: unknown;
    SelectControl: unknown;
    TextControl: unknown;
    RangeControl: unknown;
    ToggleControl: unknown;
  };
  apiFetch?: (options: { path: string }) => Promise<unknown>;
};

const roots = new WeakMap<HTMLIFrameElement, Root>();

function editorWp(): WpApi | null {
  const wp = (window as unknown as { wp?: WpApi }).wp;
  if (!wp?.blocks?.registerBlockType || !wp.element || !wp.blockEditor || !wp.components) return null;
  return wp;
}

function readStatus(error: unknown): number {
  if (!error || typeof error !== "object") return 0;
  const record = error as { status?: number; data?: { status?: number } };
  if (typeof record.data?.status === "number") return record.data.status;
  if (typeof record.status === "number") return record.status;
  return 0;
}

function attributesFrom(value: Partial<EmbedAttributes> | undefined): EmbedAttributes {
  return {
    range: sanitizeEmbedRange(value?.range),
    title: sanitizeEmbedTitle(value?.title),
    limit: sanitizeEmbedLimit(value?.limit),
    theme: sanitizeEmbedTheme(value?.theme),
  };
}

async function loadPreview(kind: EmbedKind, attributes: EmbedAttributes): Promise<Exclude<EmbedModel, { state: "loading" }>> {
  const wp = editorWp();
  if (!wp?.apiFetch) return { state: "login" };
  const params = new URLSearchParams({
    range: attributes.range,
    section: kind,
    limit: String(attributes.limit),
  });
  try {
    const body = await wp.apiFetch({ path: `/causewise/v1/analytics?${params.toString()}` });
    return interpretEmbedResponse(200, body, attributes.limit);
  } catch (error) {
    const status = readStatus(error);
    if (status === 401 || status === 403) return { state: "login" };
    return interpretEmbedResponse(status || 500, error && typeof error === "object" ? error : null, attributes.limit);
  }
}

function paintFrame(
  iframe: HTMLIFrameElement,
  kind: EmbedKind,
  attributes: EmbedAttributes,
  model: EmbedModel,
  sections?: Partial<DashboardToggles>,
) {
  const doc = iframe.contentDocument;
  if (!doc?.body) return;
  let mount = doc.getElementById("causewise-embed-root");
  if (!mount) {
    if (!doc.getElementById("causewise-embed-reset")) {
      const reset = doc.createElement("style");
      reset.id = "causewise-embed-reset";
      reset.textContent = "html,body{margin:0;background:#fff;}";
      doc.head.appendChild(reset);
    }
    ensureEmbedStyles(doc);
    mount = doc.createElement("div");
    mount.id = "causewise-embed-root";
    doc.body.appendChild(mount);
    roots.set(iframe, createRoot(mount));
  }
  const root = roots.get(iframe);
  root?.render(
    createElement(EmbedView, {
      kind,
      title: attributes.title,
      theme: attributes.theme,
      limit: attributes.limit,
      range: attributes.range,
      sections:
        kind === "dashboard"
          ? {
              stats: sections?.showStats !== false,
              traffic: sections?.showTraffic !== false,
              pages: sections?.showPages !== false,
              referrers: sections?.showReferrers !== false,
              goals: sections?.showGoals !== false,
              funnels: sections?.showFunnels !== false,
              events: sections?.showEvents !== false,
            }
          : undefined,
      state: model.state,
      error: model.state === "error" ? model.error : undefined,
      payload: model.state === "ready" ? model.payload : undefined,
    }),
  );
  window.requestAnimationFrame(() => {
    const cap = kind === "dashboard" ? 1200 : 640;
    iframe.style.height = `${Math.min(cap, Math.max(180, doc.body.scrollHeight))}px`;
  });
}

function registerEmbeds() {
  const wp = editorWp();
  if (!wp?.element || !wp.blockEditor || !wp.components || !wp.blocks) return;
  const { createElement: h, Fragment, useEffect, useRef } = wp.element;
  const { InspectorControls, useBlockProps } = wp.blockEditor;
  const { PanelBody, SelectControl, TextControl, RangeControl, ToggleControl } = wp.components;

  EMBEDS.forEach((definition) => {
    const attributes: Record<string, { type: string; default: string | number | boolean }> = {
      range: { type: "string", default: "7d" },
      title: { type: "string", default: "" },
      limit: { type: "number", default: 8 },
      theme: { type: "string", default: "light" },
    };
    if (definition.dashboard) {
      for (const [key, value] of Object.entries(DASHBOARD_TOGGLES)) {
        attributes[key] = { type: "boolean", default: value };
      }
      attributes.align = { type: "string", default: "" };
      attributes.width = { type: "string", default: "" };
      attributes.instance = { type: "string", default: "" };
    }
    const supports = definition.dashboard
      ? { html: false, anchor: false, align: ["wide", "full"] }
      : { html: false, anchor: false };
    wp.blocks?.registerBlockType(definition.block, {
      apiVersion: 3,
      title: definition.title,
      description: definition.description,
      category: "causewise",
      icon: definition.icon,
      keywords: ["causewise", "analytics", "dashboard"],
      supports,
      attributes,
      edit(props: EditProps) {
        const frame = useRef<HTMLIFrameElement | null>(null);
        const attributes = attributesFrom(props.attributes);
        const toggles = sanitizeDashboardToggles(props.attributes);
        const toggleKey = definition.dashboard ? Object.values(toggles).join(",") : "";
        useEffect(() => {
          const iframe = frame.current;
          if (!iframe) return undefined;
          let cancelled = false;
          const show = () => {
            if (cancelled || !iframe.contentDocument?.body) return;
            paintFrame(iframe, definition.kind, attributes, { state: "loading" }, toggles);
            void loadPreview(definition.kind, attributes).then((model) => {
              if (!cancelled) paintFrame(iframe, definition.kind, attributes, model, toggles);
            });
          };
          if (iframe.contentDocument?.body) show();
          else iframe.addEventListener("load", show, { once: true });
          return () => {
            cancelled = true;
            iframe.removeEventListener("load", show);
            roots.get(iframe)?.unmount();
            roots.delete(iframe);
            iframe.contentDocument?.getElementById("causewise-embed-root")?.remove();
          };
        }, [attributes.range, attributes.title, attributes.limit, attributes.theme, toggleKey]);
        useEffect(() => {
          if (!definition.dashboard) return;
          if (/^[a-z0-9_-]{6,40}$/.test(props.attributes.instance ?? "") && !(props.attributes.instance ?? "").includes("cw_api")) return;
          props.setAttributes({ instance: createDashboardInstance() });
        }, [props.attributes.instance]);
        useEffect(() => {
          if (!definition.dashboard) return;
          const align = props.attributes.align;
          const width = sanitizeEmbedWidth(props.attributes.width);
          if ((align === "wide" || align === "full") && (width.endsWith("px") || width.endsWith("%"))) {
            props.setAttributes({ width: "" });
          }
        }, [props.attributes.align, props.attributes.width]);

        const controls = [
          h(SelectControl, {
            key: "range",
            label: "Date range",
            value: attributes.range,
            options: [
              { label: "Today", value: "today" },
              { label: "7 days", value: "7d" },
              { label: "30 days", value: "30d" },
            ],
            onChange: (range: string) => props.setAttributes({ range: sanitizeEmbedRange(range) }),
          }),
          h(TextControl, {
            key: "title",
            label: "Title",
            value: attributes.title,
            help: definition.dashboard
              ? "Optional. Leave blank to use CauseWise Analytics."
              : "Optional. Leave blank to show only the section name.",
            onChange: (title: string) => props.setAttributes({ title: sanitizeEmbedTitle(title) }),
          }),
          definition.limit
            ? h(RangeControl, {
                key: "limit",
                label: "Rows",
                value: attributes.limit,
                min: 1,
                max: 20,
                onChange: (limit: number) => props.setAttributes({ limit: sanitizeEmbedLimit(limit) }),
              })
            : null,
          ...(definition.dashboard ? dashboardWidthControls(h, SelectControl, RangeControl, props) : []),
          h(SelectControl, {
            key: "theme",
            label: "Theme",
            value: attributes.theme,
            options: [
              { label: "Light", value: "light" },
              { label: "Dark", value: "dark" },
              { label: "Match the visitor's system", value: "system" },
            ],
            onChange: (theme: string) => props.setAttributes({ theme: sanitizeEmbedTheme(theme) }),
          }),
          ...(definition.dashboard && ToggleControl
            ? (
                [
                  ["showStats", "Overview statistics"],
                  ["showTraffic", "Traffic chart"],
                  ["showPages", "Top Pages"],
                  ["showReferrers", "Referrers"],
                  ["showGoals", "Goals"],
                  ["showFunnels", "Funnels"],
                  ["showEvents", "Recent Events"],
                ] as const
              ).map(([key, label]) =>
                h(ToggleControl, {
                  key,
                  label,
                  checked: toggles[key],
                  onChange: (checked: boolean) => props.setAttributes({ [key]: checked === true }),
                }),
              )
            : []),
        ];

        const width = definition.dashboard ? sanitizeEmbedWidth(props.attributes.width) : "";
        const customWidth = width.endsWith("px") || width.endsWith("%") ? width : "";
        return h(
          Fragment,
          null,
          h(InspectorControls, null, h(PanelBody, { title: "CauseWise", initialOpen: true }, controls)),
          h(
            "div",
            useBlockProps(customWidth ? { style: { width: "100%", maxWidth: customWidth } } : undefined),
            h("iframe", {
              ref: frame,
              className: "causewise-embed-frame",
              title: `${definition.title} preview`,
            }),
            h("p", { className: "causewise-embed-editor-note" }, EDITOR_NOTE),
          ),
        );
      },
      save() {
        return null;
      },
    });
  });
}

function createDashboardInstance(): string {
  const cryptoApi = globalThis.crypto;
  if (cryptoApi?.getRandomValues) {
    const bytes = new Uint8Array(8);
    cryptoApi.getRandomValues(bytes);
    return `cw${Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
  }
  return `cw${Math.random().toString(16).slice(2, 10)}${Math.random().toString(16).slice(2, 6)}`;
}

function dashboardWidthControls(
  h: WpElement["createElement"],
  SelectControl: unknown,
  RangeControl: unknown,
  props: EditProps,
) {
  const align = props.attributes.align === "wide" || props.attributes.align === "full" ? props.attributes.align : "";
  const width = sanitizeEmbedWidth(props.attributes.width);
  const choice = align || (width.endsWith("px") || width.endsWith("%") ? "custom" : "container");
  const pixels = /^(\d+)px$/.exec(width);
  const controls = [
    h(SelectControl, {
      key: "width",
      label: "Width",
      value: choice,
      options: [
        { label: "Container", value: "container" },
        { label: "Wide", value: "wide" },
        { label: "Full width", value: "full" },
        { label: "Custom maximum width", value: "custom" },
      ],
      onChange: (next: string) => {
        if (next === "wide" || next === "full") props.setAttributes({ align: next, width: "" });
        else if (next === "custom") props.setAttributes({ align: "", width: pixels ? width : "1100px" });
        else props.setAttributes({ align: "", width: "" });
      },
    }),
  ];
  if (choice === "custom" && !width.endsWith("%")) {
    controls.push(
      h(RangeControl, {
        key: "max-width",
        label: "Maximum width (px)",
        help: "The dashboard stays within its parent and does not grow past this width.",
        value: pixels ? Number(pixels[1]) : 1100,
        min: 320,
        max: 1600,
        onChange: (next: number | undefined) => {
          const clean = sanitizeEmbedWidth(next);
          if (clean) props.setAttributes({ align: "", width: clean });
        },
      }),
    );
  }
  return controls;
}

registerEmbeds();
