import csv from 'csv-parser';
import { Readable } from 'stream';
import PsychometricStatement from '../models/psychometricStatement';

const REQUIRED_FIELDS = ['statement_text', 'dimension', 'direction'];
const VALID_DIMENSIONS = ['EI', 'SN', 'TF', 'JP'];
const VALID_DIRECTIONS = ['positive', 'negative'];

const validateRow = (row: Record<string, string>) => {
  const errors: string[] = [];

  for (const field of REQUIRED_FIELDS) {
    if (!row[field] || !row[field].trim()) {
      errors.push(`Missing or empty field: "${field}"`);
    }
  }
  if (errors.length) return errors;

  if (!VALID_DIMENSIONS.includes(row.dimension.trim())) {
    errors.push(`dimension must be one of: ${VALID_DIMENSIONS.join(', ')} — got "${row.dimension}"`);
  }

  if (!VALID_DIRECTIONS.includes(row.direction.trim().toLowerCase())) {
    errors.push(`direction must be "positive" or "negative" — got "${row.direction}"`);
  }

  if (row.sequence !== undefined && row.sequence !== '') {
    if (isNaN(Number(row.sequence))) {
      errors.push(`sequence must be a number — got "${row.sequence}"`);
    }
  }

  return errors;
};

const parseCSV = (buffer: Buffer): Promise<Record<string, string>[]> => {
  return new Promise((resolve, reject) => {
    const rows: Record<string, string>[] = [];
    const stream = Readable.from(buffer);
    stream
      .pipe(csv({ mapHeaders: ({ header }: { header: string }) => header.trim() }))
      .on('data', (row: Record<string, string>) => rows.push(row))
      .on('end', () => resolve(rows))
      .on('error', (err: Error) => reject(err));
  });
};

export const importStatements = async (fileBuffer: Buffer) => {
  const rows = await parseCSV(fileBuffer);

  if (rows.length === 0) {
    const err = Object.assign(new Error('CSV file is empty or has no data rows'), { statusCode: 400 });
    throw err;
  }

  const existingTexts = new Set(
    (await PsychometricStatement.find({}, 'statement_text').lean()).map((s) =>
      s.statement_text.toLowerCase(),
    ),
  );

  const toInsert: Record<string, unknown>[] = [];
  const failed: { row: number; data: Record<string, string>; reasons: string[] }[] = [];

  rows.forEach((row, i) => {
    const rowNumber = i + 2;
    const errors = validateRow(row);

    if (errors.length) {
      failed.push({ row: rowNumber, data: row, reasons: errors });
      return;
    }

    const text = row.statement_text.trim();
    if (existingTexts.has(text.toLowerCase())) {
      failed.push({ row: rowNumber, data: row, reasons: ['Duplicate statement — already exists in database'] });
      return;
    }
    existingTexts.add(text.toLowerCase());

    toInsert.push({
      statement_text: text,
      dimension: row.dimension.trim(),
      direction: row.direction.trim().toLowerCase(),
      ...(row.sequence && row.sequence !== '' && { sequence: Number(row.sequence) }),
    });
  });

  let inserted = 0;
  if (toInsert.length > 0) {
    const result = await PsychometricStatement.insertMany(toInsert, { ordered: false });
    inserted = result.length;
  }

  return { total: rows.length, inserted, failed_count: failed.length, failed };
};
