export type BatchMassSeparatorMode = 'AUTO' | 'SEMICOLON' | 'TAB' | 'PIPE';
export type BatchMassApplyMode = 'ADD' | 'REPLACE';

export interface BatchMassColumnGuideItem {
  key: string;
  label: string;
  required: boolean;
  canUseDefault: boolean;
  note?: string;
}

export interface BatchMassDefaults {
  usuario?: string;
  tipoDeChip?: string;
  planoContrato?: string;
  status?: string;
  contaEmpresa?: string;
  conta?: string;
  dtEfetivacaoServico?: string;
  dtTerminoFidelizacao?: string;
}

export interface BatchMassPreviewRow {
  sourceLineNumber: number;
  rawLine: string;
  values: string[];
  data: Record<string, string>;
  errors: string[];
}

export interface BatchMassPreviewResult {
  separator: BatchMassSeparatorMode;
  recognizedRows: number;
  validRows: number;
  invalidRows: number;
  duplicateRows: number;
  hasHeader: boolean;
  rows: BatchMassPreviewRow[];
  parseErrors: string[];
}

const SEQUENCE_KEYS = [
  'linha',
  'chip',
  'usuario',
  'tipoDeChip',
  'planoContrato',
  'status',
  'contaEmpresa',
  'conta',
  'dtEfetivacaoServico',
  'dtTerminoFidelizacao'
] as const;

const SEQUENCE_LABELS: Record<(typeof SEQUENCE_KEYS)[number], string> = {
  linha: 'Linha',
  chip: 'Chip (ICCID)',
  usuario: 'Usuário',
  tipoDeChip: 'Tipo de Chip',
  planoContrato: 'Plano Contrato',
  status: 'Status',
  contaEmpresa: 'Empresa (Conta)',
  conta: 'Conta',
  dtEfetivacaoServico: 'Dt. Efetivação Serviço',
  dtTerminoFidelizacao: 'Dt. Término Fidelização'
};

export const BATCH_MASS_COLUMN_GUIDE: BatchMassColumnGuideItem[] = [
  { key: 'linha', label: 'Linha', required: true, canUseDefault: false, note: 'Número da linha (telefone).' },
  { key: 'chip', label: 'Chip (ICCID)', required: true, canUseDefault: false, note: 'ICCID da linha.' },
  { key: 'usuario', label: 'Usuário', required: false, canUseDefault: true, note: 'Pode vir por linha ou usar padrão.' },
  { key: 'tipoDeChip', label: 'Tipo de Chip', required: false, canUseDefault: true, note: 'Pode vir por linha ou usar padrão.' },
  { key: 'planoContrato', label: 'Plano Contrato', required: true, canUseDefault: true, note: 'Obrigatório; pode variar por linha.' },
  { key: 'status', label: 'Status', required: true, canUseDefault: true, note: 'Obrigatório; pode variar por linha.' },
  { key: 'contaEmpresa', label: 'Empresa (Conta)', required: true, canUseDefault: true, note: 'Obrigatório; pode variar por linha.' },
  { key: 'conta', label: 'Conta', required: true, canUseDefault: true, note: 'Obrigatório; pode variar por linha.' },
  { key: 'dtEfetivacaoServico', label: 'Dt. Efetivação Serviço', required: true, canUseDefault: true, note: 'Aceita YYYY-MM-DD ou DD/MM/YYYY.' },
  { key: 'dtTerminoFidelizacao', label: 'Dt. Término Fidelização', required: true, canUseDefault: true, note: 'Aceita YYYY-MM-DD ou DD/MM/YYYY.' }
];

const REQUIRED_KEYS = [
  'linha',
  'chip',
  'planoContrato',
  'status',
  'contaEmpresa',
  'conta',
  'dtEfetivacaoServico',
  'dtTerminoFidelizacao'
] as const;

const HEADER_ALIAS_TO_KEY: Array<[string, (typeof SEQUENCE_KEYS)[number]]> = [
  ['linha', 'linha'],
  ['numero linha', 'linha'],
  ['n linha', 'linha'],
  ['telefone', 'linha'],
  ['chip', 'chip'],
  ['iccid', 'chip'],
  ['chip iccid', 'chip'],
  ['usuario', 'usuario'],
  ['usuario da linha', 'usuario'],
  ['tipo de chip', 'tipoDeChip'],
  ['tipodechip', 'tipoDeChip'],
  ['plano contrato', 'planoContrato'],
  ['plano', 'planoContrato'],
  ['status', 'status'],
  ['empresa conta', 'contaEmpresa'],
  ['empresa', 'contaEmpresa'],
  ['conta empresa', 'contaEmpresa'],
  ['conta', 'conta'],
  ['dt efetivacao servico', 'dtEfetivacaoServico'],
  ['data efetivacao servico', 'dtEfetivacaoServico'],
  ['dt efetivacao', 'dtEfetivacaoServico'],
  ['efetivacao', 'dtEfetivacaoServico'],
  ['dt termino fidelizacao', 'dtTerminoFidelizacao'],
  ['data termino fidelizacao', 'dtTerminoFidelizacao'],
  ['termino fidelizacao', 'dtTerminoFidelizacao'],
  ['fidelizacao', 'dtTerminoFidelizacao']
];

function stripAccents(value: string): string {
  return value.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

function normalizeHeaderCell(value: string): string {
  return stripAccents((value ?? '').toString())
    .toLowerCase()
    // Normalize punctuation like parentheses so headers such as
    // "Chip (ICCID)" and "Empresa (Conta)" match aliases.
    .replace(/[^a-z0-9]+/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

function getSeparatorChar(mode: Exclude<BatchMassSeparatorMode, 'AUTO'>): string {
  if (mode === 'SEMICOLON') return ';';
  if (mode === 'TAB') return '\t';
  return '|';
}

function getEffectiveSeparatorForTemplate(mode: BatchMassSeparatorMode): Exclude<BatchMassSeparatorMode, 'AUTO'> {
  return mode === 'AUTO' ? 'SEMICOLON' : mode;
}

function detectSeparator(text: string): Exclude<BatchMassSeparatorMode, 'AUTO'> {
  const lines = text.split(/\r?\n/).map((x) => x.trim()).filter(Boolean);
  const first = lines[0] ?? '';
  const counts = {
    TAB: (first.match(/\t/g) ?? []).length,
    SEMICOLON: (first.match(/;/g) ?? []).length,
    PIPE: (first.match(/\|/g) ?? []).length
  } as const;

  const entries = Object.entries(counts) as Array<[Exclude<BatchMassSeparatorMode, 'AUTO'>, number]>;
  entries.sort((a, b) => b[1] - a[1]);
  return entries[0]?.[1] ? entries[0][0] : 'SEMICOLON';
}

function splitBySeparator(line: string, mode: BatchMassSeparatorMode): string[] {
  const effective: Exclude<BatchMassSeparatorMode, 'AUTO'> = mode === 'AUTO' ? detectSeparator(line) : mode;
  const sepChar = getSeparatorChar(effective);
  return line
    .split(sepChar)
    .map((x) => x.trim())
    .map((x) => (x === '""' ? '' : x));
}

function resolveHeaderKey(cell: string): (typeof SEQUENCE_KEYS)[number] | null {
  const normalized = normalizeHeaderCell(cell);
  if (!normalized) return null;

  const alias = HEADER_ALIAS_TO_KEY.find(([name]) => name === normalized);
  return alias?.[1] ?? null;
}

function maybeNormalizeDate(value: string): string {
  const raw = (value ?? '').toString().trim();
  if (!raw) return '';

  if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) return raw;
  if (/^\d{4}\/\d{2}\/\d{2}$/.test(raw)) return raw.replace(/\//g, '-');

  const m = raw.match(/^(\d{1,2})[\/-](\d{1,2})[\/-](\d{4})$/);
  if (m) {
    const day = m[1].padStart(2, '0');
    const month = m[2].padStart(2, '0');
    const year = m[3];
    return `${year}-${month}-${day}`;
  }

  return raw;
}

function normalizeRowData(data: Record<string, string>): Record<string, string> {
  const next = { ...data };
  next['linha'] = (next['linha'] ?? '').toString().trim();
  next['chip'] = (next['chip'] ?? '').toString().trim();
  next['usuario'] = (next['usuario'] ?? '').toString().trim();
  next['tipoDeChip'] = (next['tipoDeChip'] ?? '').toString().trim();
  next['planoContrato'] = (next['planoContrato'] ?? '').toString().trim();
  next['status'] = (next['status'] ?? '').toString().trim();
  next['contaEmpresa'] = (next['contaEmpresa'] ?? '').toString().trim();
  next['conta'] = (next['conta'] ?? '').toString().trim();
  next['dtEfetivacaoServico'] = maybeNormalizeDate(next['dtEfetivacaoServico'] ?? '');
  next['dtTerminoFidelizacao'] = maybeNormalizeDate(next['dtTerminoFidelizacao'] ?? '');
  return next;
}

function parseHeaderMap(values: string[]): Map<number, (typeof SEQUENCE_KEYS)[number]> {
  const map = new Map<number, (typeof SEQUENCE_KEYS)[number]>();
  values.forEach((cell, idx) => {
    const key = resolveHeaderKey(cell);
    if (key) map.set(idx, key);
  });
  return map;
}

function looksLikeDataRow(values: string[]): boolean {
  const first = (values[0] ?? '').trim();
  const second = (values[1] ?? '').trim();
  const firstDigits = first.replace(/\D/g, '');
  const secondDigits = second.replace(/\D/g, '');

  return firstDigits.length >= 8 || secondDigits.length >= 8;
}

function buildDataFromValues(
  values: string[],
  defaults: BatchMassDefaults,
  headerMap?: Map<number, (typeof SEQUENCE_KEYS)[number]>
): Record<string, string> {
  const base: Record<string, string> = {
    linha: '',
    chip: '',
    usuario: defaults.usuario?.toString().trim() ?? '',
    tipoDeChip: defaults.tipoDeChip?.toString().trim() ?? '',
    planoContrato: defaults.planoContrato?.toString().trim() ?? '',
    status: defaults.status?.toString().trim() ?? '',
    contaEmpresa: defaults.contaEmpresa?.toString().trim() ?? '',
    conta: defaults.conta?.toString().trim() ?? '',
    dtEfetivacaoServico: defaults.dtEfetivacaoServico?.toString().trim() ?? '',
    dtTerminoFidelizacao: defaults.dtTerminoFidelizacao?.toString().trim() ?? ''
  };

  if (headerMap && headerMap.size > 0) {
    values.forEach((value, idx) => {
      const key = headerMap.get(idx);
      if (!key) return;
      base[key] = value;
    });
    return normalizeRowData(base);
  }

  values.forEach((value, idx) => {
    const key = SEQUENCE_KEYS[idx];
    if (!key) return;
    base[key] = value;
  });

  return normalizeRowData(base);
}

function validatePreviewRows(rows: BatchMassPreviewRow[]): void {
  const linhaCounts = new Map<string, number>();

  rows.forEach((row) => {
    const digits = (row.data['linha'] ?? '').replace(/\D/g, '');
    if (!digits) return;
    linhaCounts.set(digits, (linhaCounts.get(digits) ?? 0) + 1);
  });

  rows.forEach((row) => {
    const errors: string[] = [];
    const linha = (row.data['linha'] ?? '').trim();
    const chip = (row.data['chip'] ?? '').trim();
    const linhaDigits = linha.replace(/\D/g, '');

    if (!linha) errors.push('Linha obrigatoria.');
    else if (!linhaDigits) errors.push('Numero de linha invalido.');
    if (!chip) errors.push('Chip (ICCID) obrigatorio.');

    REQUIRED_KEYS.forEach((key) => {
      if (key === 'linha' || key === 'chip') return;
      if (!(row.data[key] ?? '').toString().trim()) {
        if (key === 'contaEmpresa') errors.push('Empresa (Conta) obrigatoria.');
        else if (key === 'planoContrato') errors.push('Plano Contrato obrigatorio.');
        else if (key === 'dtEfetivacaoServico') errors.push('Dt. Efetivacao Servico obrigatoria.');
        else if (key === 'dtTerminoFidelizacao') errors.push('Dt. Termino Fidelizacao obrigatoria.');
        else if (key === 'conta') errors.push('Conta obrigatoria.');
        else if (key === 'status') errors.push('Status obrigatorio.');
      }
    });

    if (linhaDigits && (linhaCounts.get(linhaDigits) ?? 0) > 1) {
      errors.push('Linha duplicada no lote.');
    }

    row.errors = errors;
  });
}

export function buildBatchMassPreview(
  text: string,
  opts?: {
    separatorMode?: BatchMassSeparatorMode;
    defaults?: BatchMassDefaults;
    detectHeader?: boolean;
  }
): BatchMassPreviewResult {
  const rawText = (text ?? '').toString();
  const separatorMode = opts?.separatorMode ?? 'AUTO';
  const defaults = opts?.defaults ?? {};
  const detectHeaderEnabled = opts?.detectHeader ?? true;
  const parseErrors: string[] = [];

  const nonEmptyLines = rawText
    .split(/\r?\n/)
    .map((line, idx) => ({ line: line.trim(), sourceLineNumber: idx + 1 }))
    .filter((x) => x.line.length > 0);

  if (nonEmptyLines.length === 0) {
    return {
      separator: separatorMode === 'AUTO' ? 'SEMICOLON' : separatorMode,
      recognizedRows: 0,
      validRows: 0,
      invalidRows: 0,
      duplicateRows: 0,
      hasHeader: false,
      rows: [],
      parseErrors: []
    };
  }

  const effectiveSeparator = separatorMode === 'AUTO' ? detectSeparator(nonEmptyLines[0].line) : separatorMode;
  const splitLines = nonEmptyLines.map((entry) => ({
    ...entry,
    values: splitBySeparator(entry.line, effectiveSeparator)
  }));

  let headerMap: Map<number, (typeof SEQUENCE_KEYS)[number]> | undefined;
  let hasHeader = false;
  if (detectHeaderEnabled && splitLines.length > 0) {
    const first = splitLines[0];
    const candidate = parseHeaderMap(first.values);
    const minAliases = looksLikeDataRow(first.values) ? 4 : 2;
    if (candidate.size >= minAliases) {
      headerMap = candidate;
      hasHeader = true;
    }
  }

  const rows: BatchMassPreviewRow[] = [];
  const startIndex = hasHeader ? 1 : 0;
  for (let i = startIndex; i < splitLines.length; i++) {
    const entry = splitLines[i];
    const allEmpty = entry.values.every((v) => !v.trim());
    if (allEmpty) continue;

    const data = buildDataFromValues(entry.values, defaults, headerMap);
    rows.push({
      sourceLineNumber: entry.sourceLineNumber,
      rawLine: entry.line,
      values: entry.values,
      data,
      errors: []
    });
  }

  if (rows.length === 0 && hasHeader) {
    parseErrors.push('Nenhuma linha de dados encontrada abaixo do cabecalho.');
  }

  validatePreviewRows(rows);

  const duplicateRows = rows.filter((r) => r.errors.some((e) => e.includes('duplicada'))).length;
  const invalidRows = rows.filter((r) => r.errors.length > 0).length;

  return {
    separator: effectiveSeparator,
    recognizedRows: rows.length,
    validRows: rows.length - invalidRows,
    invalidRows,
    duplicateRows,
    hasHeader,
    rows,
    parseErrors
  };
}

export function mergeMassRows<T>(existing: T[], incoming: T[], mode: BatchMassApplyMode): T[] {
  return mode === 'REPLACE' ? [...incoming] : [...existing, ...incoming];
}

export function buildBatchMassHeaderLine(mode: BatchMassSeparatorMode = 'SEMICOLON'): string {
  const sep = getSeparatorChar(getEffectiveSeparatorForTemplate(mode));
  return SEQUENCE_KEYS.map((key) => SEQUENCE_LABELS[key]).join(sep);
}

export function buildBatchMassExampleText(mode: BatchMassSeparatorMode = 'SEMICOLON', withHeader = true): string {
  const sep = getSeparatorChar(getEffectiveSeparatorForTemplate(mode));
  const lines: string[] = [];

  if (withHeader) {
    lines.push(buildBatchMassHeaderLine(mode));
  }

  lines.push(
    [
      '11999999999',
      '8955000000000000001',
      'João',
      'eSIM',
      'SMART EMPRESAS 6GB',
      'ATIVO',
      'VIVO MACROPHONY',
      '0430237019',
      '2026-01-01',
      '2027-01-01'
    ].join(sep)
  );

  lines.push(
    [
      '11999999998',
      '8955000000000000002',
      'Maria',
      'Físico',
      'SMART EMPRESAS 10GB',
      'ATIVO',
      'VIVO MACROPHONY',
      '0430237019',
      '2026-01-02',
      '2027-01-02'
    ].join(sep)
  );

  return lines.join('\n');
}
