import xlsx from 'xlsx';
import prisma from '../services/prisma.js';
import asyncHandler from '../utils/asyncHandler.js';
import ApiError from '../utils/ApiError.js';
import { questionSchema } from '../utils/validators.js';
import { parseQuestionsFile } from '../services/import.service.js';

// Header row + a couple of example rows, matching the import parser's expected columns.
// The Practice/Exam bank is chosen in the import UI and applies to the whole file,
// so the template intentionally has no category column.
const TEMPLATE_ROWS = [
  ['question', 'option1', 'option2', 'option3', 'option4', 'correct', 'explanation', 'type'],
  [
    'What does TCP stand for?',
    'Transmission Control Protocol', 'Transfer Control Protocol', 'Transport Control Packet', 'Transmission Carrier Protocol',
    'A', 'TCP stands for Transmission Control Protocol.', 'single',
  ],
  [
    'Which are valid private IPv4 ranges? (choose two)',
    '10.0.0.0/8', '172.16.0.0/12', '8.8.8.0/24', '100.0.0.0/8',
    'A,B', 'Per RFC1918, 10.0.0.0/8 and 172.16.0.0/12 are private ranges.', 'multiple',
  ],
];

// GET /api/questions/template?format=csv|xlsx  (admin) — downloadable import template
export const downloadTemplate = asyncHandler(async (req, res) => {
  const format = req.query.format === 'xlsx' ? 'xlsx' : 'csv';
  const sheet = xlsx.utils.aoa_to_sheet(TEMPLATE_ROWS);

  if (format === 'xlsx') {
    const wb = xlsx.utils.book_new();
    xlsx.utils.book_append_sheet(wb, sheet, 'Questions');
    const buffer = xlsx.write(wb, { type: 'buffer', bookType: 'xlsx' });
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    res.setHeader('Content-Disposition', 'attachment; filename="questions-template.xlsx"');
    return res.send(buffer);
  }

  const csv = xlsx.utils.sheet_to_csv(sheet);
  res.setHeader('Content-Type', 'text/csv; charset=utf-8');
  res.setHeader('Content-Disposition', 'attachment; filename="questions-template.csv"');
  res.send(csv);
});

function validateOptions(options, type) {
  const correctCount = options.filter((o) => o.isCorrect).length;
  if (correctCount === 0) throw ApiError.badRequest('At least one option must be correct');
  if (type === 'SINGLE' && correctCount > 1) {
    throw ApiError.badRequest('Single-answer questions must have exactly one correct option');
  }
}

// ---- Admin: create approved question directly ----
export const createQuestion = asyncHandler(async (req, res) => {
  const data = questionSchema.parse(req.body);
  validateOptions(data.options, data.type);

  const question = await prisma.question.create({
    data: {
      courseId: data.courseId,
      text: data.text,
      explanation: data.explanation || null,
      type: data.type,
      category: data.category,
      status: 'APPROVED',
      createdById: req.user.id,
      approvedById: req.user.id,
      options: {
        create: data.options.map((o, i) => ({ text: o.text, isCorrect: o.isCorrect, order: i })),
      },
    },
    include: { options: true },
  });
  res.status(201).json({ success: true, question });
});

export const updateQuestion = asyncHandler(async (req, res) => {
  const data = questionSchema.partial().parse(req.body);
  const existing = await prisma.question.findUnique({ where: { id: req.params.id } });
  if (!existing) throw ApiError.notFound('Question not found');

  const updateData = {
    text: data.text ?? existing.text,
    explanation: data.explanation ?? existing.explanation,
    type: data.type ?? existing.type,
    category: data.category ?? existing.category,
  };

  if (data.options) {
    validateOptions(data.options, data.type ?? existing.type);
    await prisma.option.deleteMany({ where: { questionId: existing.id } });
    updateData.options = {
      create: data.options.map((o, i) => ({ text: o.text, isCorrect: o.isCorrect, order: i })),
    };
  }

  const question = await prisma.question.update({
    where: { id: existing.id },
    data: updateData,
    include: { options: true },
  });
  res.json({ success: true, question });
});

export const deleteQuestion = asyncHandler(async (req, res) => {
  await prisma.question.delete({ where: { id: req.params.id } });
  res.json({ success: true, message: 'Question deleted' });
});

// Admin: list questions for a course (with answers). Optional ?category=PRACTICE|EXAM filter.
export const adminListByCourse = asyncHandler(async (req, res) => {
  const { category } = req.query;
  const where = { courseId: req.params.courseId };
  if (category === 'PRACTICE' || category === 'EXAM') where.category = category;

  const questions = await prisma.question.findMany({
    where,
    orderBy: { createdAt: 'desc' },
    include: { options: { orderBy: { order: 'asc' } }, createdBy: { select: { name: true, email: true } } },
  });
  res.json({ success: true, questions });
});

// ---- User: submit a question for approval ----
export const submitUserQuestion = asyncHandler(async (req, res) => {
  const data = questionSchema.parse(req.body);
  validateOptions(data.options, data.type);

  const course = await prisma.course.findUnique({ where: { id: data.courseId } });
  if (!course) throw ApiError.badRequest('Course not found');

  const question = await prisma.question.create({
    data: {
      courseId: data.courseId,
      text: data.text,
      explanation: data.explanation || null,
      type: data.type,
      category: data.category,
      status: 'PENDING',
      createdById: req.user.id,
      options: {
        create: data.options.map((o, i) => ({ text: o.text, isCorrect: o.isCorrect, order: i })),
      },
    },
    include: { options: true },
  });
  res.status(201).json({
    success: true,
    message: 'Question submitted for admin approval',
    question,
  });
});

// ---- Admin approvals ----
export const listPending = asyncHandler(async (_req, res) => {
  const questions = await prisma.question.findMany({
    where: { status: 'PENDING' },
    orderBy: { createdAt: 'asc' },
    include: {
      options: { orderBy: { order: 'asc' } },
      course: { select: { title: true, slug: true, vendor: { select: { name: true } } } },
      createdBy: { select: { name: true, email: true } },
    },
  });
  res.json({ success: true, questions });
});

// Approve a pending question. The admin can choose which bank it lands in via
// req.body.category: 'PRACTICE' | 'EXAM' | 'BOTH' (defaults to the user's chosen bank).
// 'BOTH' approves the original into PRACTICE and clones an approved copy into EXAM.
export const approveQuestion = asyncHandler(async (req, res) => {
  const target = req.body.category;

  const existing = await prisma.question.findUnique({
    where: { id: req.params.id },
    include: { options: { orderBy: { order: 'asc' } } },
  });
  if (!existing) throw ApiError.notFound('Question not found');

  if (target === 'BOTH') {
    await prisma.question.update({
      where: { id: existing.id },
      data: { status: 'APPROVED', approvedById: req.user.id, category: 'PRACTICE' },
    });
    await prisma.question.create({
      data: {
        courseId: existing.courseId,
        text: existing.text,
        explanation: existing.explanation,
        type: existing.type,
        category: 'EXAM',
        status: 'APPROVED',
        createdById: existing.createdById,
        approvedById: req.user.id,
        options: {
          create: existing.options.map((o, i) => ({ text: o.text, isCorrect: o.isCorrect, order: i })),
        },
      },
    });
    return res.json({ success: true, message: 'Approved into both Practice and Exam banks' });
  }

  const data = { status: 'APPROVED', approvedById: req.user.id };
  if (target === 'PRACTICE' || target === 'EXAM') data.category = target;
  const question = await prisma.question.update({ where: { id: existing.id }, data });
  res.json({ success: true, question });
});

export const rejectQuestion = asyncHandler(async (req, res) => {
  const question = await prisma.question.update({
    where: { id: req.params.id },
    data: { status: 'REJECTED', approvedById: req.user.id },
  });
  res.json({ success: true, question });
});

// ---- Admin: import CSV/Excel ----
export const importQuestions = asyncHandler(async (req, res) => {
  if (!req.file) throw ApiError.badRequest('No file uploaded (field name must be "file")');
  const courseId = req.body.courseId;
  if (!courseId) throw ApiError.badRequest('courseId is required');

  // Default category for the whole file; a per-row category column overrides it.
  const defaultCategory = req.body.category === 'EXAM' ? 'EXAM' : 'PRACTICE';

  const course = await prisma.course.findUnique({ where: { id: courseId } });
  if (!course) throw ApiError.badRequest('Course not found');

  const { parsed, errors, totalRows } = parseQuestionsFile(req.file.buffer, req.file.originalname);

  let created = 0;
  for (const q of parsed) {
    await prisma.question.create({
      data: {
        courseId,
        text: q.text,
        explanation: q.explanation,
        type: q.type,
        category: q.category || defaultCategory,
        status: 'APPROVED',
        createdById: req.user.id,
        approvedById: req.user.id,
        options: { create: q.options },
      },
    });
    created += 1;
  }

  res.json({
    success: true,
    message: `Imported ${created} of ${totalRows} rows`,
    created,
    totalRows,
    skipped: errors,
  });
});
