// src/lib/backlink/normalize.ts
import type { LinkSourceSeed } from "./types";

/** Force https, lowercase host, strip a single trailing slash. "" on blank/invalid. */
export function normalizeUrl(raw: string): string {
  const s = raw.trim();
  if (!s) return "";
  const withProto = /^https?:\/\//i.test(s) ? s : `https://${s}`;
  try {
    const u = new URL(withProto);
    u.protocol = "https:";
    u.hostname = u.hostname.toLowerCase();
    let out = u.toString();
    if (out.endsWith("/") && u.pathname === "/") out = out.slice(0, -1);
    return out;
  } catch {
    return "";
  }
}

/** Registrable-ish host without leading "www.", lowercased. "" on blank/invalid. */
export function domainNorm(raw: string): string {
  const s = raw.trim();
  if (!s) return "";
  const withProto = /^https?:\/\//i.test(s) ? s : `https://${s}`;
  try {
    return new URL(withProto).hostname.toLowerCase().replace(/^www\./, "");
  } catch {
    return "";
  }
}

export interface MergeLog {
  domainNorm: string;
  kept: string; // platform kept
  dropped: string; // platform dropped
}

const daRank = (da: number | null): number =>
  da == null ? Number.NEGATIVE_INFINITY : da;

/** Collapse sources sharing a dedup key (default: domainNorm). Keep highest DA, union themes/tags. */
export function dedupeMerge(
  input: LinkSourceSeed[],
  keyOf: (s: LinkSourceSeed) => string = (s) => s.domainNorm,
): {
  sources: LinkSourceSeed[];
  merges: MergeLog[];
} {
  const byKey = new Map<string, LinkSourceSeed>();
  const merges: MergeLog[] = [];
  for (const src of input) {
    const key = keyOf(src);
    const existing = byKey.get(key);
    if (!existing) {
      byKey.set(key, src);
      continue;
    }
    const keepNew = daRank(src.da) > daRank(existing.da);
    const winner = keepNew ? src : existing;
    const loser = keepNew ? existing : src;
    byKey.set(key, {
      ...winner,
      themes: [...new Set([...existing.themes, ...src.themes])],
      tags: [...new Set([...existing.tags, ...src.tags])],
      flags: {
        toxic: existing.flags.toxic || src.flags.toxic,
        disavow: existing.flags.disavow || src.flags.disavow,
      },
    });
    merges.push({
      domainNorm: src.domainNorm,
      kept: winner.platform,
      dropped: loser.platform,
    });
  }
  return { sources: [...byKey.values()], merges };
}
