import { parse } from 'csv-parse/sync';
import xlsx from 'xlsx';
import ApiError from '../utils/ApiError.js';

/**
 * Accepted columns (case-insensitive, flexible):
 *   question (required)
 *   option1..option6 OR optionA..optionF (at least 2 required)
 *   correct  -> letters/numbers of correct options, e.g. "A" or "A,C" or "1,3"
 *   explanation (optional)
 *   type     -> "single" | "multiple" (optional, auto-detected from correct count)
 */

function normalizeKey(k) {
  return String(k || '').trim().toLowerCase().replace(/[\s_-]/g, '');
}

function rowsFromBuffer(buffer, originalname) {
  const name = (originalname || '').toLowerCase();
  if (name.endsWith('.csv')) {
    return parse(buffer.toString('utf-8'), {
      columns: true,
      skip_empty_lines: true,
      trim: true,
    });
  }
  // xlsx / xls
  const wb = xlsx.read(buffer, { type: 'buffer' });
  const sheet = wb.Sheets[wb.SheetNames[0]];
  return xlsx.utils.sheet_to_json(sheet, { defval: '' });
}

function extractOptions(row) {
  // Map normalized keys to value
  const map = {};
  for (const [k, v] of Object.entries(row)) {
    map[normalizeKey(k)] = v;
  }

  const options = [];
  const letters = ['a', 'b', 'c', 'd', 'e', 'f'];
  for (let i = 0; i < 6; i++) {
    const byNum = map[`option${i + 1}`];
    const byLetter = map[`option${letters[i]}`];
    const val = byNum !== undefined && byNum !== '' ? byNum : byLetter;
    if (val !== undefined && String(val).trim() !== '') {
      options.push(String(val).trim());
    }
  }
  return { map, options };
}

function parseCorrect(correctRaw, optionCount) {
  // Supports "A", "A,C", "1,3", "1;3"
  const tokens = String(correctRaw || '')
    .split(/[,;|]/)
    .map((t) => t.trim())
    .filter(Boolean);

  const indices = new Set();
  for (const tok of tokens) {
    if (/^[a-zA-Z]$/.test(tok)) {
      indices.add(tok.toUpperCase().charCodeAt(0) - 65);
    } else if (/^\d+$/.test(tok)) {
      indices.add(parseInt(tok, 10) - 1);
    }
  }
  return [...indices].filter((i) => i >= 0 && i < optionCount);
}

export function parseQuestionsFile(buffer, originalname) {
  let rows;
  try {
    rows = rowsFromBuffer(buffer, originalname);
  } catch (e) {
    throw ApiError.badRequest(`Could not parse file: ${e.message}`);
  }

  if (!rows || rows.length === 0) {
    throw ApiError.badRequest('File contains no rows');
  }

  const parsed = [];
  const errors = [];

  rows.forEach((row, idx) => {
    const { map, options } = extractOptions(row);
    const text = String(map.question || map.questiontext || '').trim();

    if (!text) {
      errors.push({ row: idx + 2, error: 'Missing question text' });
      return;
    }
    if (options.length < 2) {
      errors.push({ row: idx + 2, error: 'Need at least 2 options' });
      return;
    }

    const correctIdx = parseCorrect(map.correct || map.answer || map.correctanswer, options.length);
    if (correctIdx.length === 0) {
      errors.push({ row: idx + 2, error: 'No valid correct answer specified' });
      return;
    }

    const typeRaw = normalizeKey(map.type);
    const type =
      typeRaw === 'multiple' || correctIdx.length > 1 ? 'MULTIPLE' : 'SINGLE';

    // Optional per-row category column (category / mode / bank). Null = use import default.
    const catRaw = normalizeKey(map.category || map.mode || map.bank);
    const category = catRaw === 'exam' ? 'EXAM' : catRaw === 'practice' ? 'PRACTICE' : null;

    parsed.push({
      text,
      explanation: String(map.explanation || '').trim() || null,
      type,
      category,
      options: options.map((opt, i) => ({
        text: opt,
        isCorrect: correctIdx.includes(i),
        order: i,
      })),
    });
  });

  return { parsed, errors, totalRows: rows.length };
}
