import { clean, normalizeStatus } from '../holder-master/holder-master-import';

export type PlateImportRow = {
  sourceId?: number;
  holderCode: string;
  plateName: string;
  brakeName: string;
  plateCode: string;
  brakeType: string;
  brakeTypeCode: string;
  loadCarryingType: string;
  loadCarryingCode: string;
  mountingType: string;
  mountingCode: string;
  fittingSize: string;
  fittingSizeCode: string;
  bearingType: string;
  bearingCode: string;
  fittingType: string;
  fittingCode: string;
  yokeName?: string;
  yokeCode?: string;
  status: string;
};

export type PlateImportResult = {
  rows: PlateImportRow[];
  errors: string[];
};

type RawRow = Record<string, string>;

const headerMap: Record<string, keyof PlateImportRow> = {
  ID: 'sourceId',
  HOLDERCODE: 'holderCode',
  PLATENAME: 'plateName',
  BRAKENAME: 'brakeName',
  PLATECODE: 'plateCode',
  BRAKETYPEWITHORWITHOUT: 'brakeType',
  BRAKETYPEWITHORWITHOUTCODE: 'brakeTypeCode',
  LOADCARRYINGTYPE: 'loadCarryingType',
  LOADCARRYINGCODE: 'loadCarryingCode',
  MOUNTINGTYPE: 'mountingType',
  MOUNTINGCODE: 'mountingCode',
  FITTINGSIZE: 'fittingSize',
  FITTINGSIZECODE: 'fittingSizeCode',
  BEARINGTYPE: 'bearingType',
  BEARINGCODE: 'bearingCode',
  FITTINGTYPE: 'fittingType',
  FITTINGCODE: 'fittingCode',
  YOKENAME: 'yokeName',
  YOKECODE: 'yokeCode',
  PLATESTATUS: 'status',
  STATUS: 'status',
};

export const plateCsvHeaders = [
  'Id',
  'HOLDER CODE',
  'PLATE NAME',
  'BRAKE NAME',
  'PLATE CODE',
  'BRAKE TYPE(WITH OR WITHOUT)',
  'BRAKE TYPE(WITH OR WITHOUT) CODE',
  'LOAD CARRYING TYPE',
  'LOAD CARRYING CODE',
  'MOUNTING TYPE',
  'MOUNTING CODE',
  'FITTING SIZE',
  'FITTING SIZE CODE',
  'BEARING TYPE',
  'BEARING CODE',
  'FITTING TYPE',
  'FITTING CODE',
  'Yoke Name',
  'Yoke Code',
  'PLATE STATUS',
];

export function parsePlateImport(text: string): PlateImportResult {
  const delimiter = detectDelimiter(text);
  const rows = parseDelimited(text, delimiter);
  const [headers = [], ...records] = rows;
  const normalizedHeaders = headers.map(normalizeHeader);
  const result: PlateImportRow[] = [];
  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).toUpperCase();
    const plateName = clean(raw.plateName);
    const brakeName = clean(raw.brakeName);
    const plateCode = clean(raw.plateCode).toUpperCase();

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

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

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

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

    if (plateCode.length > 128 || /\s/.test(plateCode)) {
      errors.push(`Row ${rowNumber}: PLATE CODE is invalid.`);
      return;
    }

    if (!holderCode || !plateName || !brakeName || !plateCode) {
      return;
    }

    result.push({
      sourceId: parseInteger(raw.sourceId),
      holderCode,
      plateName,
      brakeName,
      plateCode,
      brakeType: clean(raw.brakeType),
      brakeTypeCode: clean(raw.brakeTypeCode).toUpperCase(),
      loadCarryingType: clean(raw.loadCarryingType),
      loadCarryingCode: clean(raw.loadCarryingCode).toUpperCase(),
      mountingType: clean(raw.mountingType),
      mountingCode: clean(raw.mountingCode).toUpperCase(),
      fittingSize: clean(raw.fittingSize),
      fittingSizeCode: clean(raw.fittingSizeCode).toUpperCase(),
      bearingType: clean(raw.bearingType),
      bearingCode: clean(raw.bearingCode).toUpperCase(),
      fittingType: clean(raw.fittingType),
      fittingCode: clean(raw.fittingCode).toUpperCase(),
      yokeName: clean(raw.yokeName) || undefined,
      yokeCode: clean(raw.yokeCode) || undefined,
      status: normalizeStatus(raw.status),
    });
  });

  return { rows: result, errors };
}

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 parseDelimited(text: string, delimiter: ',' | '\t'): string[][] {
  if (delimiter === '\t') {
    return text
      .replace(/^\uFEFF/, '')
      .split(/\r?\n/)
      .filter((line) => line.length)
      .map((line) => line.split('\t').map(stripDelimitedCell));
  }

  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;
}

function stripDelimitedCell(value: string): string {
  const trimmed = value.trim();

  if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
    return trimmed.slice(1, -1).replace(/""/g, '"').trim();
  }

  return trimmed;
}
