export type HolderImportRow = {
  sourceId?: number;
  holderInchValue: number;
  holderInch: string;
  holderName: string;
  holderCode: string;
  bodyName: string;
  bodyCode: string;
  yorkeName?: string;
  yorkeCode?: string;
  yorkeLoad?: number;
  status: string;
};

export type HolderImportResult = {
  rows: HolderImportRow[];
  errors: string[];
};

type RawRow = Record<string, string>;

const headerMap: Record<string, keyof HolderImportRow> = {
  ID: 'sourceId',
  HOLDERINCH: 'holderInch',
  HOLDERNAME: 'holderName',
  HOLDERCODE: 'holderCode',
  BODYNAME: 'bodyName',
  BODYCODE: 'bodyCode',
  YORKENAME: 'yorkeName',
  YORKECODE: 'yorkeCode',
  YORKELOAD: 'yorkeLoad',
  HOLDERSTATUS: 'status',
  STATUS: 'status',
};

export const holderCsvHeaders = [
  'Id',
  'HOLDER INCH',
  'HOLDER NAME',
  'HOLDER CODE',
  'BODY NAME',
  'BODY CODE',
  'YORKE NAME',
  'YORKE CODE',
  'YORKE LOAD',
  'HOLDER STATUS',
];

export function parseHolderImport(text: string): HolderImportResult {
  const delimiter = detectDelimiter(text);
  const rows = parseDelimited(text, delimiter);
  const [headers = [], ...records] = rows;
  const normalizedHeaders = headers.map(normalizeHeader);
  const result: HolderImportRow[] = [];
  const errors: string[] = [];

  records.forEach((record, rowIndex) => {
    const raw: RawRow = {};

    normalizedHeaders.forEach((header, index) => {
      if (!header || !headerMap[header]) {
        return;
      }

      raw[headerMap[header]] = clean(record[index]);
    });

    if (Object.values(raw).every((value) => !value)) {
      return;
    }

    const rowNumber = rowIndex + 2;
    const holderCode = clean(raw.holderCode);
    const holderName = clean(raw.holderName);
    const holderInch = normalizeHolderInch(raw.holderInch);
    const holderInchValue = parseInchValue(raw.holderInch);
    const bodyName = clean(raw.bodyName);
    const bodyCode = clean(raw.bodyCode);

    if (!holderCode) {
      errors.push(`Row ${rowNumber}: HOLDER CODE is required.`);
    }

    if (!holderName) {
      errors.push(`Row ${rowNumber}: HOLDER NAME is required.`);
    }

    if (!holderInch) {
      errors.push(`Row ${rowNumber}: HOLDER INCH is required.`);
    }

    if (!holderInchValue) {
      errors.push(`Row ${rowNumber}: HOLDER INCH must be between 1 and 15.`);
    }

    if (!bodyName) {
      errors.push(`Row ${rowNumber}: BODY NAME is required.`);
    }

    if (!bodyCode) {
      errors.push(`Row ${rowNumber}: BODY CODE is required.`);
    }

    if (
      !holderCode ||
      !holderName ||
      !holderInch ||
      !holderInchValue ||
      !bodyName ||
      !bodyCode
    ) {
      return;
    }

    result.push({
      sourceId: parseInteger(raw.sourceId),
      holderInchValue,
      holderInch,
      holderName,
      holderCode,
      bodyName,
      bodyCode,
      yorkeName: clean(raw.yorkeName) || undefined,
      yorkeCode: clean(raw.yorkeCode) || undefined,
      yorkeLoad: parseNumber(raw.yorkeLoad),
      status: normalizeStatus(raw.status),
    });
  });

  return { rows: result, errors };
}

export function normalizeHolderInch(value?: string): string {
  const inchValue = parseInchValue(value);

  if (!inchValue) {
    return '';
  }

  return `${inchValue} INCH`;
}

export function normalizeStatus(value?: string): string {
  const cleaned = clean(value).toLowerCase();

  if (!cleaned) {
    return 'Active';
  }

  return cleaned === 'inactive' ? 'Inactive' : 'Active';
}

export function clean(value?: string | number | null): string {
  return String(value ?? '').trim();
}

function detectDelimiter(text: string): ',' | '\t' {
  const firstLine = text.split(/\r?\n/, 1)[0] || '';
  const tabCount = (firstLine.match(/\t/g) || []).length;
  const commaCount = (firstLine.match(/,/g) || []).length;

  return tabCount > commaCount ? '\t' : ',';
}

function normalizeHeader(header: string): string {
  return clean(header).replace(/[^a-z0-9]/gi, '').toUpperCase();
}

function parseInteger(value?: string): number | undefined {
  const parsed = Number.parseInt(clean(value), 10);
  return Number.isFinite(parsed) ? parsed : undefined;
}

function parseNumber(value?: string): number | undefined {
  const parsed = Number.parseFloat(clean(value));
  return Number.isFinite(parsed) ? parsed : undefined;
}

export function parseInchValue(value?: string): number | undefined {
  const match = clean(value).match(/(\d+(?:\.\d+)?)/);

  if (!match) {
    return undefined;
  }

  const parsed = Number.parseFloat(match[1]);

  if (!Number.isFinite(parsed) || parsed < 1 || parsed > 15) {
    return undefined;
  }

  return parsed;
}

function parseDelimited(text: string, delimiter: ',' | '\t'): string[][] {
  const rows: string[][] = [];
  let current = '';
  let row: string[] = [];
  let inQuotes = false;

  for (let index = 0; index < text.length; index += 1) {
    const character = text[index];
    const nextCharacter = text[index + 1];

    if (character === '"' && inQuotes && nextCharacter === '"') {
      current += '"';
      index += 1;
      continue;
    }

    if (character === '"') {
      inQuotes = !inQuotes;
      continue;
    }

    if (character === delimiter && !inQuotes) {
      row.push(current);
      current = '';
      continue;
    }

    if ((character === '\n' || character === '\r') && !inQuotes) {
      if (character === '\r' && nextCharacter === '\n') {
        index += 1;
      }

      row.push(current);
      rows.push(row);
      row = [];
      current = '';
      continue;
    }

    current += character;
  }

  if (current || row.length) {
    row.push(current);
    rows.push(row);
  }

  return rows;
}
