import csv from 'csv-parser';
import { Readable } from 'stream';
import Question from '../models/question';

const REQUIRED_FIELDS = ['question_text', 'option_a', 'option_b', 'option_c', 'option_d', 'correct_option'];

type CsvRow = Record<string, string>;

// ─── Validation ───────────────────────────────────────────────────────────────

const validateRow = (row: CsvRow): 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;

  const correct = row.correct_option.trim();
  const optionValues = [row.option_a.trim(), row.option_b.trim(), row.option_c.trim(), row.option_d.trim()];
  if (!optionValues.includes(correct)) {
    errors.push(`correct_option "${correct}" does not match any of the provided options`);
  }

  if (row.difficulty !== undefined && row.difficulty !== '') {
    if (!['easy', 'medium', 'hard'].includes(row.difficulty.trim().toLowerCase())) {
      errors.push(`difficulty must be one of: easy, medium, hard — got "${row.difficulty}"`);
    }
  }

  if (row.points !== undefined && row.points !== '') {
    const pts = Number(row.points);
    if (isNaN(pts) || pts <= 0) errors.push(`points must be a positive number — got "${row.points}"`);
  }

  return errors;
};

// ─── CSV Parsing ──────────────────────────────────────────────────────────────

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

// ─── Main Service ─────────────────────────────────────────────────────────────

export const importQuestions = async (fileBuffer: Buffer) => {
  const rows = await parseCSV(fileBuffer);
  if (rows.length === 0) throw Object.assign(new Error('CSV file is empty or has no data rows'), { statusCode: 400 });

  const existingTexts = new Set(
    (await Question.find({}, 'question_text').lean()).map((q) => q.question_text.toLowerCase()),
  );

  const toInsert: object[] = [];
  const failed: { row: number; data: CsvRow; 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 questionText = row.question_text.trim();
    if (existingTexts.has(questionText.toLowerCase())) {
      failed.push({ row: rowNumber, data: row, reasons: ['Duplicate question — already exists in database'] });
      return;
    }
    existingTexts.add(questionText.toLowerCase());

    toInsert.push({
      question_text: questionText,
      option_a: row.option_a.trim(),
      option_b: row.option_b.trim(),
      option_c: row.option_c.trim(),
      option_d: row.option_d.trim(),
      correct_option: row.correct_option.trim(),
      ...(row.difficulty && row.difficulty !== '' && { difficulty: row.difficulty.trim().toLowerCase() }),
      ...(row.category && row.category !== '' && { category: row.category.trim() }),
      ...(row.tags && row.tags !== '' && { tags: row.tags.split(',').map((t) => t.trim()).filter(Boolean) }),
      ...(row.points && row.points !== '' && { points: Number(row.points) }),
    });
  });

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

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