import type { StoreSiteBlock } from "@alqove/api-client";

/**
 * Block `data` is `additionalProperties: true` in the contract — the API
 * validates each type's shape, but TypeScript can't see that. These readers
 * keep the render path total: a malformed value renders as absent instead of
 * throwing halfway down the page.
 */

export type BlockData = Record<string, unknown>;

export function blockData(block: StoreSiteBlock): BlockData {
  return (block.data ?? {}) as BlockData;
}

export function str(data: BlockData, key: string): string | null {
  const value = data[key];
  return typeof value === "string" && value.trim() !== "" ? value : null;
}

export function bool(data: BlockData, key: string, fallback = false): boolean {
  const value = data[key];
  return typeof value === "boolean" ? value : fallback;
}

export function num(data: BlockData, key: string, fallback: number): number {
  const value = data[key];
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
}

export function list<T>(data: BlockData, key: string): T[] {
  const value = data[key];
  return Array.isArray(value) ? (value as T[]) : [];
}

/**
 * Seller-authored hrefs are rendered as links, so anything that isn't an
 * in-page anchor, a same-origin path, or an http(s)/mailto/tel URL is dropped —
 * `javascript:` in particular must never reach an `href`.
 */
export function safeHref(raw: string | null): string | null {
  if (!raw) return null;
  const value = raw.trim();
  if (value === "") return null;

  if (value.startsWith("#") || value.startsWith("/")) return value;

  try {
    const url = new URL(value);
    return ["http:", "https:", "mailto:", "tel:"].includes(url.protocol) ? value : null;
  } catch {
    return null;
  }
}

/** Same rule for image sources — only absolute http(s) URLs are rendered. */
export function safeImageUrl(raw: string | null): string | null {
  if (!raw) return null;
  try {
    const url = new URL(raw.trim());
    return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
  } catch {
    return null;
  }
}

/** Split a seller's plain-text body into paragraphs on blank lines. */
export function paragraphs(body: string | null): string[] {
  if (!body) return [];
  return body
    .split(/\n{2,}/)
    .map((p) => p.trim())
    .filter(Boolean);
}
