import { getBlockComponent } from "@/components/page-blocks";
import { cloneBlockContent } from "@/lib/cloneBlockContent";
import { resolveBlockSlug, type ComponentSlugRecord } from "@/lib/blockSlug";
import { createPageBlockInstanceId } from "@/lib/pageBlockInstance";
import { pasteDebug, pasteLogError } from "@/lib/pasteDebug";
import { normalizePastedText } from "@/lib/pasteText";
import type { PageBlock } from "@/admin/types";

export type PastedComponentPayload = {
  componentId: number;
  componentName: string;
  componentSlug?: string;
  content?: Record<string, unknown>;
};

export type PasteValidationCategory =
  | "empty"
  | "unsupported_clipboard"
  | "invalid_json"
  | "missing_required"
  | "invalid_content"
  | "unsupported_type";

export type PastePreviewSummary = {
  componentName: string;
  componentSlug: string;
  componentId: number;
  fieldCount: number;
  imageUrls: string[];
  hasPreviewRenderer: boolean;
  inComponentLibrary: boolean;
  statusLabel: "Valid" | "Valid with warnings";
};

export type ParsePasteSuccess = {
  ok: true;
  block: PageBlock;
  warnings: string[];
  summary: PastePreviewSummary;
};

export type ParsePasteFailure = {
  ok: false;
  error: string;
  category: PasteValidationCategory;
  /** Missing or invalid field name, when applicable. */
  field?: string;
};

export type ParsePasteResult = ParsePasteSuccess | ParsePasteFailure;

const IMAGE_URL_PATTERN = /\.(png|jpe?g|gif|webp|svg|avif)(\?|$)/i;
const IMAGE_KEY_PATTERN = /image|photo|thumbnail|icon|logo|avatar|banner|background/i;

export const PASTE_VALIDATION_LABELS: Record<PasteValidationCategory, string> = {
  empty: "Empty paste",
  unsupported_clipboard: "Unsupported clipboard content",
  invalid_json: "Invalid JSON format",
  missing_required: "Missing required fields",
  invalid_content: "Invalid content",
  unsupported_type: "Unsupported component type",
};

/** Shown when pasted text is empty or only whitespace. */
export const PASTE_COMPONENT_EMPTY_ERROR = "Paste component JSON is empty.";

/** Shown when the system clipboard has no usable text (empty or whitespace-only). */
export const PASTE_COMPONENT_CLIPBOARD_EMPTY_ERROR =
  "Clipboard is empty. Copy a component from the Components module (Copy button) or copy a page block first.";

/** Shown when clipboard holds plain text, emojis, rich text, etc. */
export const PASTE_COMPONENT_UNSUPPORTED_ERROR =
  "Clipboard content is not supported. Please paste valid component JSON copied from the Components module or a page block.";

/** Shown when text looks like JSON but cannot be parsed or is not a component object. */
export const PASTE_COMPONENT_INVALID_JSON_ERROR =
  "Invalid component JSON format. Copy a component from the Components module (Copy button) and try again.";

/** Shown when pasted JSON is an array instead of a single component object. */
export const PASTE_COMPONENT_ARRAY_ERROR =
  "Component JSON must be a single object, not an array. Copy one component at a time.";

export const PASTE_COMPONENT_EXAMPLE = {
  componentId: 1,
  componentName: "Hero",
  componentSlug: "hero",
  content: {},
} as const;

export const PASTE_COMPONENT_EXAMPLE_JSON = JSON.stringify(PASTE_COMPONENT_EXAMPLE, null, 2);

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

type PastedTextShape = "object" | "array" | "other";

function classifyPastedTextShape(text: string): PastedTextShape {
  const first = text.charAt(0);
  if (first === "{") return "object";
  if (first === "[") return "array";
  return "other";
}

function coerceComponentId(value: unknown): number | null {
  if (typeof value === "number" && Number.isFinite(value)) {
    return Number.isInteger(value) ? value : Math.trunc(value);
  }
  if (typeof value === "string") {
    const trimmed = value.trim();
    if (!trimmed) return null;
    const parsed = Number(trimmed);
    if (Number.isFinite(parsed)) return Number.isInteger(parsed) ? parsed : Math.trunc(parsed);
  }
  return null;
}

function failure(
  error: string,
  category: PasteValidationCategory,
  field?: string
): ParsePasteFailure {
  return { ok: false, error, category, field };
}

function countContentFields(content: Record<string, unknown>): number {
  let count = 0;
  const walk = (value: unknown): void => {
    if (Array.isArray(value)) {
      value.forEach(walk);
      return;
    }
    if (typeof value === "object" && value !== null) {
      count += Object.keys(value).length;
      Object.values(value).forEach(walk);
    }
  };
  walk(content);
  return count;
}

function looksLikeImageUrl(value: string): boolean {
  const trimmed = value.trim();
  if (!trimmed) return false;
  if (IMAGE_URL_PATTERN.test(trimmed)) return true;
  if (/^https?:\/\//i.test(trimmed) && IMAGE_KEY_PATTERN.test(trimmed)) return true;
  if (trimmed.startsWith("/") && IMAGE_URL_PATTERN.test(trimmed)) return true;
  return false;
}

export function extractImageUrlsFromContent(content: Record<string, unknown>, limit = 6): string[] {
  const urls: string[] = [];
  const seen = new Set<string>();

  const walk = (value: unknown, keyHint = ""): void => {
    if (urls.length >= limit) return;
    if (typeof value === "string") {
      if (looksLikeImageUrl(value)) {
        const normalized = value.trim();
        if (!seen.has(normalized)) {
          seen.add(normalized);
          urls.push(normalized);
        }
      }
      return;
    }
    if (Array.isArray(value)) {
      value.forEach((item) => walk(item, keyHint));
      return;
    }
    if (typeof value === "object" && value !== null) {
      for (const [key, nested] of Object.entries(value)) {
        walk(nested, key);
      }
    }
  };

  walk(content);
  return urls;
}

function buildPreviewSummary(
  block: PageBlock,
  components: ComponentSlugRecord[],
  warnings: string[]
): PastePreviewSummary {
  const content = block.content ?? {};
  return {
    componentName: block.componentName,
    componentSlug: block.componentSlug ?? "",
    componentId: block.componentId,
    fieldCount: countContentFields(content),
    imageUrls: extractImageUrlsFromContent(content),
    hasPreviewRenderer: Boolean(getBlockComponent(block.componentSlug ?? "")),
    inComponentLibrary: components.some((c) => c.id === block.componentId),
    statusLabel: warnings.length > 0 ? "Valid with warnings" : "Valid",
  };
}

function normalizeContentField(content: unknown): { ok: true; value: Record<string, unknown> } | { ok: false; error: string } {
  if (content === undefined || content === null) {
    return { ok: true, value: {} };
  }
  if (isPlainObject(content)) {
    return { ok: true, value: content };
  }
  if (typeof content === "string") {
    const trimmed = normalizePastedText(content);
    if (!trimmed) return { ok: true, value: {} };
    try {
      const parsed = JSON.parse(trimmed);
      if (isPlainObject(parsed)) return { ok: true, value: parsed };
      return { ok: false, error: '"content" must be a JSON object (parsed string was not an object).' };
    } catch {
      return {
        ok: false,
        error: '"content" must be a JSON object. The string value could not be parsed as JSON.',
      };
    }
  }
  if (Array.isArray(content)) {
    return { ok: false, error: '"content" must be a JSON object, not an array.' };
  }
  return { ok: false, error: '"content" must be a JSON object when provided.' };
}

/**
 * Parse and validate pasted component JSON. Returns a ready-to-insert PageBlock or a clear error.
 */
export function parsePastedComponentJson(
  text: string,
  components: ComponentSlugRecord[]
): ParsePasteResult {
  const trimmed = normalizePastedText(text);
  pasteDebug("parse:start", { length: trimmed.length, preview: trimmed.slice(0, 120) });

  if (!trimmed) {
    return failure(PASTE_COMPONENT_EMPTY_ERROR, "empty");
  }

  const shape = classifyPastedTextShape(trimmed);
  if (shape === "other") {
    pasteDebug("parse:unsupported-content", { preview: trimmed.slice(0, 80) });
    return failure(PASTE_COMPONENT_UNSUPPORTED_ERROR, "unsupported_clipboard");
  }
  if (shape === "array") {
    pasteDebug("parse:unsupported-array", { preview: trimmed.slice(0, 80) });
    return failure(PASTE_COMPONENT_ARRAY_ERROR, "invalid_json");
  }

  let parsed: unknown;
  try {
    parsed = JSON.parse(trimmed);
  } catch (e) {
    pasteLogError("parse:json-syntax-error", e);
    return failure(PASTE_COMPONENT_INVALID_JSON_ERROR, "invalid_json");
  }

  if (!isPlainObject(parsed)) {
    pasteDebug("parse:not-object", { type: typeof parsed });
    return failure(PASTE_COMPONENT_INVALID_JSON_ERROR, "invalid_json");
  }

  const { componentId: rawComponentId, componentName, componentSlug, content } = parsed;

  if (rawComponentId === undefined || rawComponentId === null) {
    return failure(
      'Missing required field "componentId". Copy component JSON from the Component Library (Copy button).',
      "missing_required",
      "componentId"
    );
  }

  const componentId = coerceComponentId(rawComponentId);
  if (componentId === null) {
    pasteDebug("parse:invalid-component-id", { rawComponentId });
    return failure(
      `"componentId" must be a number. Check the copied JSON and try again.`,
      "missing_required",
      "componentId"
    );
  }

  if (componentName === undefined || componentName === null) {
    return failure(
      'Missing required field "componentName". Copy component JSON from the Component Library (Copy button).',
      "missing_required",
      "componentName"
    );
  }
  if (typeof componentName !== "string" || !componentName.trim()) {
    return failure(
      '"componentName" must be a non-empty text label for the component.',
      "missing_required",
      "componentName"
    );
  }

  if (componentSlug !== undefined && componentSlug !== null) {
    if (typeof componentSlug !== "string" || !componentSlug.trim()) {
      return failure(
        '"componentSlug" must be a non-empty string when provided.',
        "invalid_content",
        "componentSlug"
      );
    }
  }

  const contentResult = normalizeContentField(content);
  if (contentResult.ok === false) {
    pasteDebug("parse:invalid-content", content);
    return failure(contentResult.error, "invalid_content", "content");
  }

  const lookup: PastedComponentPayload = {
    componentId,
    componentName: componentName.trim(),
    componentSlug: typeof componentSlug === "string" ? componentSlug.trim() : undefined,
    content: contentResult.value,
  };

  const resolvedSlug = resolveBlockSlug(lookup, components);
  const warnings: string[] = [];

  const hasRenderer = Boolean(getBlockComponent(resolvedSlug));
  const inLibrary = components.some((c) => c.id === componentId);

  if (!hasRenderer && !inLibrary) {
    return failure(
      `Unsupported component type "${lookup.componentName}". Copy JSON from the Component Library or an existing page block.`,
      "unsupported_type",
      "componentSlug"
    );
  }

  if (!inLibrary) {
    warnings.push(`Component #${componentId} is not in the component library.`);
  }

  if (!hasRenderer) {
    warnings.push(`No layout preview for "${resolvedSlug}". You can still insert this component.`);
  }

  let block: PageBlock;
  try {
    block = {
      // Always mint a new instance — paste must never reuse the source block's identity.
      instanceId: createPageBlockInstanceId(),
      componentId: lookup.componentId,
      componentName: lookup.componentName,
      componentSlug: resolvedSlug,
      content: cloneBlockContent(lookup.content ?? {}, resolvedSlug),
    };
  } catch (e) {
    pasteLogError("parse:clone-content-failed", e);
    const message = e instanceof Error ? e.message : "Could not prepare component content.";
    return failure(`Could not prepare component content: ${message}`, "invalid_content", "content");
  }

  const summary = buildPreviewSummary(block, components, warnings);

  pasteDebug("parse:success", {
    instanceId: block.instanceId,
    componentId: block.componentId,
    componentName: block.componentName,
    componentSlug: block.componentSlug,
    warnings,
  });

  return { ok: true, block, warnings, summary };
}
