const RESERVED = new Set(['p', 'reel', 'reels', 'explore', 'stories', 'tv', 's', 'accounts']);

/** Normalize one token (@handle / instagram.com URL / bare name) to a valid IG
 * username, or null if it isn't one. Lowercases, strips @ and URL chrome, rejects
 * invalid/reserved tokens. */
function toHandle(token: string): string | null {
  let t = token.trim();
  if (!t) return null;
  const m = t.match(/instagram\.com\/([^/?#]+)/i);
  if (m) t = m[1]!;
  t = t.replace(/^@/, '').toLowerCase();
  if (!/^[a-z0-9._]{1,30}$/.test(t) || RESERVED.has(t)) return null;
  return t;
}

/** Minimal RFC4180 CSV parser: rows of cells, honoring "quoted" fields with
 * embedded commas/newlines and "" escaped quotes. Strips a leading BOM. Pure. */
function parseCsv(text: string): string[][] {
  const s = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
  const rows: string[][] = [];
  let row: string[] = [];
  let cell = '';
  let inQuotes = false;
  for (let i = 0; i < s.length; i += 1) {
    const c = s[i]!;
    if (inQuotes) {
      if (c === '"') {
        if (s[i + 1] === '"') { cell += '"'; i += 1; }
        else inQuotes = false;
      } else cell += c;
    } else if (c === '"') inQuotes = true;
    else if (c === ',') { row.push(cell); cell = ''; }
    else if (c === '\n' || c === '\r') {
      if (c === '\r' && s[i + 1] === '\n') i += 1;
      row.push(cell); rows.push(row); row = []; cell = '';
    } else cell += c;
  }
  if (cell !== '' || row.length) { row.push(cell); rows.push(row); }
  return rows;
}

export interface ChannelImport {
  username: string;
  category: string | null;
}

/** Parse a pasted blob OR the client's Google-Sheets CSV export into a deduped
 * list of {username, category}.
 *
 * For the structured sheet — detected by a header row containing 채널명 and 링크 —
 * handles come ONLY from those columns (so post-count / 떡상-count / per-reel-URL
 * cells in other columns are never mistaken for channels), the 링크 column wins over
 * a mistyped 채널명, and category comes from the 카테고리 column.
 *
 * Anything else (plain @handle/URL/name paste) falls back to loose token parsing
 * across newlines, commas, and TABs — NOT spaces — with a null category. Pure. */
export function parseChannelImport(raw: string): ChannelImport[] {
  return parseChannelImportWithSkipped(raw).items;
}

/** Same as parseChannelImport, but also returns the non-empty lines/cells that did NOT
 * parse into a valid handle (spaces, Korean-only names, reserved words, >30 chars), so
 * the UI can tell the user WHICH rows were dropped instead of a silent count. Skipped is
 * capped at 100 to bound the payload. Pure. */
export function parseChannelImportWithSkipped(raw: string): { items: ChannelImport[]; skipped: string[] } {
  const out: ChannelImport[] = [];
  const seen = new Set<string>();
  const skipped: string[] = [];
  const push = (username: string | null, category: string | null): void => {
    if (!username || seen.has(username)) return;
    seen.add(username);
    out.push({ username, category });
  };
  const skip = (s: string): void => {
    const t = s.trim();
    if (t && skipped.length < 100) skipped.push(t);
  };

  const rows = parseCsv(raw);
  const hi = rows.findIndex((r) => r.some((c) => c.trim() === '채널명') && r.some((c) => c.includes('링크')));
  if (hi >= 0) {
    const hdr = rows[hi]!;
    const ci = hdr.findIndex((c) => c.trim() === '채널명');
    const li = hdr.findIndex((c) => c.includes('링크'));
    const gi = hdr.findIndex((c) => c.includes('카테고리'));
    for (const r of rows.slice(hi + 1)) {
      const username = toHandle(r[li] ?? '') ?? toHandle(r[ci] ?? '');
      if (username) push(username, gi >= 0 ? (r[gi]?.trim() || null) : null);
      else skip((r[li] ?? '').trim() || (r[ci] ?? '').trim());
    }
    return { items: out, skipped };
  }

  for (const token of raw.split(/[\n\r,\t]+/)) {
    const u = toHandle(token);
    if (u) push(u, null);
    else skip(token);
  }
  return { items: out, skipped };
}

/** Deduped, normalized IG username list (handles / URLs / sheet CSV). Pure. */
export function parseChannelList(raw: string): string[] {
  return parseChannelImport(raw).map((c) => c.username);
}
