import type {
  ClientData,
  ClientSearchRow,
  ClientRow,
  ClientSource,
  ClientRecordType,
} from "@/lib/api/types/clients.types";

// "-", "n/a", "NA", "" → "" so the form doesn't pre-fill junk
const clean = (v: string | null | undefined): string => {
  if (!v) return "";
  const t = v.trim();
  if (!t || ["-", "n/a", "na", "N/A", "NA"].includes(t)) return "";
  return t;
};

// Raw paged-all row → dropdown row
export function mapClientToSearchRow(c: ClientData): ClientSearchRow {
  return {
    id: c.id,
    company_name: c.company_name ?? "",
    client_type: c.client_type ?? "Client",
    contact_primary: clean(c.contact_primary),
    mobile_no: clean(c.mobile_no),
    telephone: clean(c.telephone),
    email_id: clean(c.email_id),
    Address: clean(c.Address),
  };
}

export function mapClientsToSearchRows(rows: ClientData[]): ClientSearchRow[] {
  return rows.map(mapClientToSearchRow);
}

// ═══════════════════════════════════════════════════════════════════════
// 🔹 ADDED — Clients module (list page) mappers. Everything above is
// your existing logic, untouched.
// ═══════════════════════════════════════════════════════════════════════

// ─── Source chips (same palette as previous-nc) ────────────────────────
export const CLIENT_SOURCE_CONFIG: Record<
  ClientSource,
  { label: string; bg: string; color: string; border: string }
> = {
  QRS: { label: 'QRS', bg: '#eef2ff', color: '#4338ca', border: '#c7d2fe' },
  TQS: { label: 'TQS', bg: '#ecfeff', color: '#0e7490', border: '#a5f3fc' },
};

// ─── Record type chips ─────────────────────────────────────────────────
export const CLIENT_TYPE_CONFIG: Record<
  ClientRecordType,
  { label: string; bg: string; color: string; border: string; icon: string }
> = {
  Client: {
    label: 'Client',
    bg: '#f0fdfa',
    color: '#0f766e',
    border: '#99f6e4',
    icon: '🏢',
  },
  Surveillance: {
    label: 'Surveillance',
    bg: '#fffbeb',
    color: '#b45309',
    border: '#fde68a',
    icon: '👁',
  },
};

// ─── Status chips ──────────────────────────────────────────────────────
export function getStatusMeta(status: number | null | undefined) {
  if (status === null || status === undefined) {
    return { label: '—', bg: '#f1f5f9', color: '#64748b', dot: '#94a3b8' };
  }
  return Number(status) === 1
    ? { label: 'Active', bg: '#f0fdf4', color: '#15803d', dot: '#22c55e' }
    : { label: 'Inactive', bg: '#fef2f2', color: '#b91c1c', dot: '#ef4444' };
}

/**
 * standard_name arrives as a JSON string of standard IDs: "[\"5\",\"4\",\"6\"]".
 * The legacy `standards` lookup table isn't exposed yet, so we render the raw
 * ids. Once you expose GET /standards, map id → name here and everything
 * downstream picks it up.
 */
export function parseStandards(raw: string | null | undefined): string[] {
  if (!raw) return [];
  const s = String(raw).trim();
  if (!s) return [];
  try {
    const parsed = JSON.parse(s);
    if (Array.isArray(parsed)) return parsed.map((x) => String(x)).filter(Boolean);
  } catch {
    // not JSON — treat as plain text, possibly comma separated
    return s.split(',').map((x) => x.trim()).filter(Boolean);
  }
  return [];
}

export function formatDateWithHint(d?: string | Date | null): {
  main: string;
  hint: string;
} {
  if (!d) return { main: '—', hint: '' };
  const dt = typeof d === 'string' ? new Date(d) : d;
  if (isNaN(dt.getTime())) return { main: '—', hint: '' };
  return {
    main: dt.toLocaleDateString('en-GB', {
      day: '2-digit',
      month: 'short',
      year: 'numeric',
    }),
    hint: dt.toLocaleTimeString('en-GB', {
      hour: '2-digit',
      minute: '2-digit',
    }),
  };
}

export function firstInitial(name: string | null | undefined): string {
  const clean = (name || '').replace(/^(Mr\.?|Ms\.?|Mrs\.?|Dr\.?)\s*/i, '').trim();
  return clean ? clean[0].toUpperCase() : '?';
}

const GRADIENTS = [
  'linear-gradient(135deg,#6366f1,#8b5cf6)',
  'linear-gradient(135deg,#0f766e,#14b8a6)',
  'linear-gradient(135deg,#ea580c,#f59e0b)',
  'linear-gradient(135deg,#be123c,#f43f5e)',
  'linear-gradient(135deg,#0369a1,#0ea5e9)',
  'linear-gradient(135deg,#15803d,#22c55e)',
];

export function avatarGradient(seed: string | number): string {
  const s = String(seed);
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return GRADIENTS[h % GRADIENTS.length];
}

/** Stable key — ids repeat across QRS and TQS and across the two tables. */
export function rowKey(row: ClientRow): string {
  return `${row.source}-${row.client_type}-${row.id}`;
}

export function clientCode(row: ClientRow): string {
  const prefix = row.client_type === 'Surveillance' ? 'SUR' : 'CLI';
  return `${prefix}-${row.source}-${String(row.id).padStart(6, '0')}`;
}

// ─── 🔹 Signed document helpers ────────────────────────────────────────

/** True when the row actually has a signed document path stored. */
export function hasSignedDoc(row: ClientRow): boolean {
  return Boolean((row.signed_docs || '').trim());
}

/**
 * Display name of the signed doc — same rule as the backend: prefer the
 * stored `signeddocsname`, fall back to the file name from the path.
 * Surveillance rows have neither, so this returns null for them.
 */
export function signedDocName(row: ClientRow): string | null {
  const label = (row.signeddocsname || '').trim();
  if (label) return label;
  const path = (row.signed_docs || '').trim();
  if (!path) return null;
  const last = path.split(/[\\/]/).pop() || '';
  return last || null;
}

/**
 * Resolve a dynamic column key to its raw value.
 * Mirrors mapColumnKeyToValue in previous-nc.mappers.
 */
export function mapColumnKeyToValue(key: string, row: ClientRow): any {
  switch (key) {
    case 'client_code':
      return clientCode(row);
    case 'company_name':
      return row.company_name ?? '—';
    case 'source':
      return row.source;
    case 'client_type':
      return row.client_type;
    case 'contact_primary':
      return row.contact_primary ?? '—';
    case 'designationpr':
      return row.designationpr ?? '—';
    case 'email_id':
      return row.email_id ?? '—';
    case 'telephone':
      return row.telephone ?? row.mobile_no ?? '—';
    case 'mobile_no':
      return row.mobile_no ?? row.telephone ?? '—';
    case 'address':
    case 'Address':
      return row.Address ?? '—';
    case 'company_sector':
      return row.company_sector ?? '—';
    case 'standard_name':
      return row.standard_name;
    case 'trade_license':
      return row.trade_license ?? '—';
    case 'signed_docs':
    case 'signeddocsname':
      return signedDocName(row) ?? '—';
    case 'status':
      return row.status;
    case 'created_at':
      return row.created_at ?? null;
    case 'updated_at':
      return row.updated_at ?? null;
    default:
      return (row as any)[key] ?? '—';
  }
}

// ═══════════════════════════════════════════════════════════════════════
// 🔹 ADDED v2 — duplicate handling for the client search dropdown.
// The legacy CRM has one "Client" master row + one "Surveillance" row per
// audit cycle, often with the address appended to the name
// ("… — PO BOX NO:235485, Dubai, UAE"). These helpers group those rows by
// a normalized name and keep the cleanest one, WITHOUT touching the CRM.
// ═══════════════════════════════════════════════════════════════════════

/**
 * Uppercased grouping key with symbols, address tails, legal suffixes and
 * emirate names stripped.
 * "GOLDEN LINK CONTRACTING & DECORATION LLC",
 * "GOLDEN LINK CONTRACTING & DECORATION" and
 * "GOLDEN LINK CONTRACTING & DECORATION Abu dhabi"
 * all produce the SAME key → ONE client in the dropdown.
 * Used ONLY for grouping — never shown to the user.
 */
export function normalizeCompanyKey(name: string | null | undefined): string {
  if (!name) return "";
  let s = String(name).toUpperCase();
  // Everything after an em/en dash is address junk in the legacy data.
  s = s.split(/[—–]/)[0];
  // Drop inline "P.O. BOX 12345" fragments.
  s = s.replace(/P\.?\s*O\.?\s*BOX[\s:.]*\d*/g, " ");
  // Dots removed entirely so "L.L.C" === "LLC".
  s = s.replace(/\./g, "");
  // Every other symbol ("/", ",", "·", "-", …) becomes a space.
  s = s.replace(/[^A-Z0-9&\s]/g, " ");
  s = s.replace(/\s+/g, " ").trim();
  // 🔹 Strip legal suffixes + emirate names from the END, repeatedly, so
  // "X LLC", "X Abu Dhabi", "X L.L.C Dubai UAE" and "X Branch" all group
  // as ONE client. Only the TAIL is stripped — names STARTING with a city
  // ("DUBAI CONTRACTING") are untouched.
  const TAIL =
    /\s+(LLC|WLL|CO|COMPANY|EST|ESTABLISHMENT|FZE|FZC|FZCO|LTD|LIMITED|BRANCH|BR|UAE|U A E|ABU DHABI|DUBAI|SHARJAH|AJMAN|FUJAIRAH|RAS AL KHAIMAH|RAK|UMM AL QUWAIN|UAQ|AL AIN)$/;
  while (TAIL.test(s)) {
    const next = s.replace(TAIL, "").trim();
    if (!next) break; // never strip a name down to nothing
    s = next;
  }
  return s;
}

/** The name to SHOW: real name kept, "— address" tail and "./" junk removed. */
export function displayCompanyName(name: string | null | undefined): string {
  if (!name) return "";
  let s = String(name).split(/[—–]/)[0];
  s = s.replace(/\s*\.\/\s*/g, " ");            // stray "./" sequences
  s = s.replace(/^[\s./\\·,-]+/, "");           // leading junk
  s = s.replace(/[\s./\\·,-]+$/, "");           // trailing junk
  return s.replace(/\s+/g, " ").trim();
}

/**
 * One row per real company. Which duplicate wins:
 *   1. client_type "Client" beats "Surveillance"  (the master record)
 *   2. a name written like the Trade License (ends in LLC / WLL / …)
 *   3. a name without the address tail / junk symbols
 *   4. the row with more contact info filled
 * Grouping is per source (QRS/TQS), so a company certified in both still
 * shows once per source — drop `src` from the key to collapse across both.
 */
/** How "good" a duplicate is — Client master first, licensed-style name, most contact info. */
export function clientRowScore(r: ClientData): number {
  let s = 0;
  if ((r.client_type ?? "") === "Client") s += 100;
  const n = r.company_name ?? "";
  // 🔹 prefer the legally written name (ends with LLC/WLL/EST/…) so the
  // SELECTABLE option and the prefilled Trade-License field start correct
  if (/\b(L\.?L\.?C|W\.?L\.?L|F\.?Z\.?E|F\.?Z\.?C\.?O?|EST|LTD|LIMITED)\.?\s*$/i.test(n.trim()))
    s += 15;
  if (!/[—–]/.test(n)) s += 30;
  if (!/[./\\·,]/.test(n)) s += 20;
  s -= Math.min(n.length / 10, 10); // shorter = cleaner
  if (clean(r.contact_primary)) s += 5;
  if (clean(r.email_id)) s += 5;
  if (clean(r.mobile_no) || clean(r.telephone)) s += 3;
  return s;
}

export function dedupeClientRows(rows: ClientData[]): ClientData[] {
  const best = new Map<string, ClientData>();
  for (const r of rows) {
    const key = normalizeCompanyKey(r.company_name);
    if (!key) continue;
    const src = r.source ?? "";
    const mapKey = `${src}|${key}`;
    const cur = best.get(mapKey);
    if (!cur || clientRowScore(r) > clientRowScore(cur)) best.set(mapKey, r);
  }
  return Array.from(best.values());
}

/** A company with its best record first and every duplicate row after it. */
export interface ClientDuplicateGroup {
  key: string;
  primary: ClientData;
  duplicates: ClientData[];
}

/**
 * Group rows by normalized company name (case, symbols, "— address"
 * tails, legal suffixes and emirate names all ignored). `primary` = the
 * record to select — the initial "Client" row when it exists;
 * `duplicates` = every other variant, meant for display only (rendered
 * as DISABLED options in the dropdown).
 */
export function groupDuplicateClients(
  rows: ClientData[],
): ClientDuplicateGroup[] {
  const buckets = new Map<string, ClientData[]>();
  for (const r of rows) {
    const key = normalizeCompanyKey(r.company_name);
    if (!key) continue;
    const mapKey = `${r.source ?? ""}|${key}`;
    const arr = buckets.get(mapKey);
    if (arr) arr.push(r);
    else buckets.set(mapKey, [r]);
  }
  return Array.from(buckets.entries()).map(([key, arr]) => {
    const sorted = [...arr].sort(
      (a, b) => clientRowScore(b) - clientRowScore(a),
    );
    return { key, primary: sorted[0], duplicates: sorted.slice(1) };
  });
}

/**
 * Drop-in replacement for mapClientsToSearchRows in the dropdown:
 * deduped + tidy display names. The original function stays untouched.
 */
export function mapClientsToSearchRowsDeduped(
  rows: ClientData[],
): ClientSearchRow[] {
  return dedupeClientRows(rows).map((r) =>
    mapClientToSearchRow({
      ...r,
      company_name: displayCompanyName(r.company_name),
    }),
  );
}