/**
 * Shared artist-name parsing.
 *
 * Music tags routinely cram several artists into one string using a variety of
 * separators ("A & B", "A; B", "A, B", "A / B", "A x B") and feature markers
 * ("A feat. B", "A ft. B", "Song (feat. B)"). Historically these were stored as
 * a single combined artist row (e.g. "Booker & ЗАМАЙ"), which both duplicated
 * the real artists and produced bogus two-artists-in-one entries.
 *
 * This module is the single source of truth for breaking such a string into the
 * individual artists it actually names. The FIRST artist is treated as the main
 * artist; the rest are collaborators/features. Used by the scanner (at ingest)
 * and by the split-collab-artists migration (to clean existing rows).
 */

// Collaboration separators that join co-billed artists: ; / & , and the
// space-delimited word "x" ("Баста x Capella"). NOTE: matched case-insensitively
// and only as a standalone token so it doesn't chop names that merely contain x.
const SEPARATOR_RE = /\s*(?:;|\/|&|,)\s*|\s+x\s+/i;

// Feature/versus markers. Everything after the marker is a featured artist (or a
// list of them, which is split again on SEPARATOR_RE by the caller).
const FEAT_SPLIT_RE = /\s+(?:feat(?:uring)?\.?|ft\.?|vs\.?)\s+/i;

// Parenthesized feature marker inside a track title: "Song (feat. Guest)".
const TITLE_FEAT_RE = /[\(\[]\s*(?:feat(?:uring)?\.?|ft\.?|vs\.?)\s+([^\)\]]+)[\)\]]/i;

/** Case-insensitive, order-preserving dedupe. */
function dedupe(names: string[]): string[] {
  const seen = new Set<string>();
  const out: string[] = [];
  for (const raw of names) {
    const n = raw.trim();
    if (!n) continue;
    const key = n.toLowerCase();
    if (seen.has(key)) continue;
    seen.add(key);
    out.push(n);
  }
  return out;
}

/**
 * Split a raw artist string into the individual artists it names, in billing
 * order. Handles feature markers and all collaboration separators. Returns a
 * deduped list; a plain single artist yields a one-element array.
 */
export function splitArtistNames(rawArtist: string): string[] {
  if (!rawArtist) return [];
  // Feature markers first: "A feat. B & C" -> ["A", "B & C"], then each chunk is
  // split on collaboration separators.
  const featChunks = rawArtist.split(FEAT_SPLIT_RE);
  const parts: string[] = [];
  for (const chunk of featChunks) {
    parts.push(...chunk.split(SEPARATOR_RE));
  }
  return dedupe(parts);
}

/**
 * Parse the main + featured artists for a track from its raw artist string and
 * (optionally) its title. First named artist is main; everything else is
 * featured. `featured` excludes the main artist and is deduped.
 */
export function parseArtists(
  rawArtist: string,
  rawTitle = ''
): { main: string; featured: string[] } {
  const fromArtist = splitArtistNames(rawArtist || 'Unknown Artist');

  // Pull any "(feat. X)" group out of the title and split it too.
  const titleMatch = rawTitle.match(TITLE_FEAT_RE);
  const fromTitle = titleMatch ? splitArtistNames(titleMatch[1]) : [];

  const all = dedupe([...fromArtist, ...fromTitle]);
  if (all.length === 0) return { main: rawArtist.trim() || 'Unknown Artist', featured: [] };

  const main = all[0];
  const mainKey = main.toLowerCase();
  const featured = all.slice(1).filter((a) => a.toLowerCase() !== mainKey);
  return { main, featured };
}
