const db = require('../db/db');
const { stripTags } = require('../utils/sanitize');

/* helper: find or create topic by name within a subject */
function resolveTopicId(subjectId, topicId, topicName) {
  if (topicId) return Number(topicId);
  if (!topicName?.trim()) return null;
  const name = topicName.trim();
  const existing = db.prepare('SELECT id FROM topics WHERE subject_id = ? AND LOWER(name) = LOWER(?)').get(subjectId, name);
  if (existing) return existing.id;
  return db.prepare('INSERT INTO topics (subject_id, name) VALUES (?, ?)').run(subjectId, name).lastInsertRowid;
}

/* ── GET /api/questions?subject=bio&topic_id=1&sort=diff_asc&page=1&limit=50 */
function list(req, res) {
  const { subject, topic_id, sort, source_type, q, difficulty, type } = req.query;
  const limit  = Math.min(500, Math.max(1, Number(req.query.limit) || 100));
  const page   = Math.max(1, Number(req.query.page) || 1);
  const offset = (page - 1) * limit;

  const subj = subject
    ? db.prepare('SELECT id FROM subjects WHERE slug = ?').get(subject)
    : null;
  if (subject && !subj) return res.status(404).json({ error: 'Subject not found' });

  const ORDER = {
    date_desc:  'q.id DESC',
    date_asc:   'q.id ASC',
    diff_asc:   'q.difficulty ASC, q.id DESC',
    diff_desc:  'q.difficulty DESC, q.id DESC',
  };
  const orderBy = ORDER[sort] || 'q.id DESC';

  let where = 'WHERE 1=1';
  const args = [];
  if (subj)        { where += ' AND q.subject_id = ?';              args.push(subj.id); }
  if (topic_id)    { where += ' AND q.topic_id = ?';                args.push(topic_id); }
  if (source_type) { where += ' AND q.source_type = ?';             args.push(source_type); }
  if (difficulty)  { where += ' AND q.difficulty = ?';              args.push(Number(difficulty)); }
  if (type)        { where += ' AND q.type = ?';                    args.push(type); }
  if (q?.trim())   { where += ' AND q.text LIKE ?';                 args.push(`%${q.trim()}%`); }

  const { total } = db.prepare(`SELECT COUNT(*) AS total FROM questions q ${where}`).get(...args);

  const sql = `
    SELECT q.id, q.text, q.type, q.correct_text, q.difficulty, q.explanation, q.image,
           q.year, q.source_type,
           t.name AS topic, t.id AS topic_id,
           s.name AS subject_name, s.slug AS subject_slug,
           (SELECT json_group_array(json_object(
             'id', o.id, 'text', o.text, 'is_correct', o.is_correct, 'order_index', o.order_index, 'match_pair', o.match_pair
           ) ORDER BY o.order_index)
            FROM options o WHERE o.question_id = q.id) AS options_json
    FROM questions q
    LEFT JOIN topics   t ON t.id = q.topic_id
    LEFT JOIN subjects s ON s.id = q.subject_id
    ${where}
    ORDER BY ${orderBy} LIMIT ? OFFSET ?
  `;

  const rows = db.prepare(sql).all(...args, limit, offset).map(r => ({
    ...r,
    options: JSON.parse(r.options_json || '[]'),
    options_json: undefined,
  }));

  res.json({ rows, total, page, limit });
}

/* ── POST /api/questions ─────────────────────────────────────────────── */
function create(req, res) {
  const { subject_slug, topic_id, topic_name, type = 'single', correct_text, difficulty = 1, explanation, options, image } = req.body;
  const text = stripTags((req.body.text || '').trim());

  if (!subject_slug || !text) return res.status(400).json({ error: 'subject_slug and text are required' });
  if (text.length > 2000) return res.status(400).json({ error: 'text too long (max 2000 chars)' });
  if (type !== 'short_answer' && !options?.length)
    return res.status(400).json({ error: 'options required for this question type' });
  if (type !== 'short_answer' && type !== 'matching' && !options.some(o => o.is_correct))
    return res.status(400).json({ error: 'At least one option must be correct' });

  const subj = db.prepare('SELECT id FROM subjects WHERE slug = ?').get(subject_slug);
  if (!subj) return res.status(404).json({ error: 'Subject not found' });

  const resolvedTopicId = resolveTopicId(subj.id, topic_id, topic_name);

  try {
    const qid = db.transaction(() => {
      const { lastInsertRowid } = db.prepare(
        'INSERT INTO questions (subject_id, topic_id, text, type, correct_text, difficulty, explanation, image) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
      ).run(subj.id, resolvedTopicId, text, type, type === 'short_answer' ? (correct_text || null) : null, difficulty, explanation || null, image || null);

      const insertOpt = db.prepare(
        'INSERT INTO options (question_id, text, is_correct, order_index, match_pair) VALUES (?, ?, ?, ?, ?)'
      );
      options.forEach((o, i) => insertOpt.run(lastInsertRowid, o.text, type === 'matching' ? 0 : (o.is_correct ? 1 : 0), i, o.match_pair || null));
      return lastInsertRowid;
    })();
    res.status(201).json({ id: qid });
  } catch (err) {
    console.error('[question create]', err.message);
    res.status(500).json({ error: 'Ошибка создания вопроса' });
  }
}

/* ── POST /api/questions/:id/duplicate ──────────────────────────────── */
function duplicate(req, res) {
  const q = db.prepare(`
    SELECT q.*, s.slug AS subject_slug FROM questions q
    LEFT JOIN subjects s ON s.id = q.subject_id
    WHERE q.id = ?
  `).get(req.params.id);
  if (!q) return res.status(404).json({ error: 'Question not found' });

  const opts = db.prepare('SELECT text, is_correct, order_index FROM options WHERE question_id = ? ORDER BY order_index').all(q.id);

  try {
    const newId = db.transaction(() => {
      const { lastInsertRowid } = db.prepare(
        'INSERT INTO questions (subject_id, topic_id, text, difficulty, explanation, type, correct_text, image) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'
      ).run(q.subject_id, q.topic_id, q.text + ' (копия)', q.difficulty, q.explanation, q.type, q.correct_text, q.image);

      const ins = db.prepare('INSERT INTO options (question_id, text, is_correct, order_index) VALUES (?, ?, ?, ?)');
      opts.forEach(o => ins.run(lastInsertRowid, o.text, o.is_correct, o.order_index));
      return lastInsertRowid;
    })();
    res.status(201).json({ id: newId });
  } catch (err) {
    console.error('[question duplicate]', err.message);
    res.status(500).json({ error: 'Ошибка дублирования вопроса' });
  }
}

/* ── PUT /api/questions/:id ──────────────────────────────────────────── */
function update(req, res) {
  const { type, correct_text, difficulty, explanation, topic_id, topic_name, options, image } = req.body;
  const text = req.body.text !== undefined ? stripTags(String(req.body.text).trim()) : undefined;
  const qid = req.params.id;

  if (text !== undefined && text.length > 2000)
    return res.status(400).json({ error: 'text too long (max 2000 chars)' });

  const q = db.prepare('SELECT id, subject_id, type AS oldType FROM questions WHERE id = ?').get(qid);
  if (!q) return res.status(404).json({ error: 'Question not found' });

  const resolvedTopicId = resolveTopicId(q.subject_id, topic_id, topic_name);
  const qtype = type || q.oldType || 'single';

  try {
    db.transaction(() => {
      db.prepare(
        'UPDATE questions SET text = ?, type = ?, correct_text = ?, difficulty = ?, explanation = ?, topic_id = ?, image = ? WHERE id = ?'
      ).run(text, qtype, qtype === 'short_answer' ? (correct_text || null) : null, difficulty, explanation || null, resolvedTopicId, image !== undefined ? (image || null) : db.prepare('SELECT image FROM questions WHERE id = ?').get(qid)?.image, qid);

      if (options?.length) {
        if (qtype !== 'matching' && !options.some(o => o.is_correct))
          throw Object.assign(new Error('At least one option must be correct'), { status: 400 });

        db.prepare('DELETE FROM options WHERE question_id = ?').run(qid);
        const ins = db.prepare(
          'INSERT INTO options (question_id, text, is_correct, order_index, match_pair) VALUES (?, ?, ?, ?, ?)'
        );
        options.forEach((o, i) => ins.run(qid, o.text, qtype === 'matching' ? 0 : (o.is_correct ? 1 : 0), i, o.match_pair || null));
      }
    })();
    res.json({ id: Number(qid) });
  } catch (err) {
    console.error('[question update]', err.message);
    res.status(err.status || 500).json({ error: err.status ? err.message : 'Ошибка обновления' });
  }
}

/* ── DELETE /api/questions/:id ───────────────────────────────────────── */
function remove(req, res) {
  const q = db.prepare('SELECT id FROM questions WHERE id = ?').get(req.params.id);
  if (!q) return res.status(404).json({ error: 'Question not found' });
  db.prepare('DELETE FROM questions WHERE id = ?').run(req.params.id);
  res.json({ deleted: Number(req.params.id) });
}

/* ── POST /api/questions/import  (CSV upload) ────────────────────────── */
/* CSV format (semicolon-separated, first row = header):
   subject_slug;topic;text;difficulty;type;opt1;c1;opt2;c2;opt3;c3;opt4;c4;correct_text;explanation;year
   - type: single | multi | true_false | short_answer
   - c1-c4: 1 = correct, 0 = wrong (ignored for short_answer)
   - correct_text: used only for short_answer type
*/
function importCSV(req, res) {
  if (!req.file) return res.status(400).json({ error: 'CSV file required' });

  const raw = req.file.buffer.toString('utf-8').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
  const lines = raw.split('\n').map(l => l.trim()).filter(l => l.length);
  if (lines.length < 2) return res.status(400).json({ error: 'CSV is empty or has only header' });

  // skip header row
  const dataLines = lines.slice(1);
  const errors = [];
  let imported = 0;

  const insQ   = db.prepare('INSERT INTO questions (subject_id, topic_id, text, type, correct_text, difficulty, explanation, year) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
  const insOpt = db.prepare('INSERT INTO options (question_id, text, is_correct, order_index) VALUES (?, ?, ?, ?)');

  try {
    db.transaction(() => {
      for (let i = 0; i < dataLines.length; i++) {
        const cols = dataLines[i].split(';');
        const [subject_slug, topic, text, diffStr, type = 'single',
               opt1 = '', c1 = '0', opt2 = '', c2 = '0',
               opt3 = '', c3 = '0', opt4 = '', c4 = '0',
               correct_text = '', explanation = '', yearStr = ''] = cols.map(c => c.trim());

        if (!subject_slug || !text) { errors.push(`Строка ${i + 2}: пропущен subject_slug или text`); continue; }

        const subj = db.prepare('SELECT id FROM subjects WHERE slug = ?').get(subject_slug);
        if (!subj) { errors.push(`Строка ${i + 2}: неизвестный subject_slug «${subject_slug}»`); continue; }

        const difficulty = Math.min(3, Math.max(1, Number(diffStr) || 1));
        const year       = yearStr ? Number(yearStr) || null : null;
        const qtype      = ['single', 'multi', 'true_false', 'short_answer'].includes(type) ? type : 'single';
        const topicId    = resolveTopicId(subj.id, null, topic || null);

        const isShort = qtype === 'short_answer';
        const options = [
          { text: opt1, is_correct: c1 === '1' },
          { text: opt2, is_correct: c2 === '1' },
          { text: opt3, is_correct: c3 === '1' },
          { text: opt4, is_correct: c4 === '1' },
        ].filter(o => o.text.length > 0);

        if (!isShort && options.length < 2) { errors.push(`Строка ${i + 2}: нужно минимум 2 варианта ответа`); continue; }
        if (!isShort && !options.some(o => o.is_correct)) { errors.push(`Строка ${i + 2}: нет правильного варианта (укажи 1 в столбце c1–c4)`); continue; }

        const { lastInsertRowid: qid } = insQ.run(
          subj.id, topicId, text, qtype,
          isShort ? (correct_text || null) : null,
          difficulty, explanation || null, year
        );
        if (!isShort) options.forEach((o, idx) => insOpt.run(qid, o.text, o.is_correct ? 1 : 0, idx));
        imported++;
      }
    })();
  } catch (e) {
    console.error('[question import]', e.message);
    return res.status(500).json({ error: 'Ошибка импорта' });
  }

  res.json({ imported, errors });
}

module.exports = { list, create, duplicate, update, remove, importCSV };
