'use strict';
const db = require('../db/db');
const { awardXP } = require('./gamificationController');

/* ── Helpers ─────────────────────────────────────────────────────────── */
const MAX_V = { H:1, C:4, N:3, O:2, P:5, S:6, Cl:1, Na:1, Ca:2, K:1, Mg:2, Fe:3, Br:1, I:1, F:1 };

function hillFormula(atoms) {
  const cnt = {};
  for (const a of atoms) cnt[a.s] = (cnt[a.s] || 0) + 1;
  const parts = [];
  if (cnt.C) { parts.push('C' + (cnt.C > 1 ? cnt.C : '')); delete cnt.C; }
  if (cnt.H) { parts.push('H' + (cnt.H > 1 ? cnt.H : '')); delete cnt.H; }
  for (const el of Object.keys(cnt).sort()) parts.push(el + (cnt[el] > 1 ? cnt[el] : ''));
  return parts.join('');
}

function valencyIssues(atoms, bonds) {
  const sums = {};
  for (const b of bonds) {
    sums[b.f] = (sums[b.f] || 0) + b.o;
    sums[b.t] = (sums[b.t] || 0) + b.o;
  }
  return atoms
    .filter(a => (sums[a.id] || 0) > (MAX_V[a.s] ?? 4))
    .map(a => ({ id: a.id, symbol: a.s, used: sums[a.id] || 0, max: MAX_V[a.s] ?? 4 }));
}

/* ── Prepared statements ─────────────────────────────────────────────── */
const stmts = {
  getElements:    db.prepare('SELECT * FROM bio_elements ORDER BY radius ASC'),
  getMolecules:   db.prepare("SELECT id,formula,name_ru,name_lat,category,difficulty,description,topic_tags,atoms_json,bonds_json FROM bio_molecules WHERE is_library=1 ORDER BY difficulty,name_ru"),
  getMolCat:      db.prepare("SELECT id,formula,name_ru,name_lat,category,difficulty,description,topic_tags,atoms_json,bonds_json FROM bio_molecules WHERE is_library=1 AND category=? ORDER BY difficulty,name_ru"),
  getMolSearch:   db.prepare("SELECT id,formula,name_ru,name_lat,category,difficulty,description,topic_tags,atoms_json,bonds_json FROM bio_molecules WHERE is_library=1 AND (name_ru LIKE ? OR formula LIKE ?) ORDER BY difficulty,name_ru"),
  getMolById:     db.prepare('SELECT * FROM bio_molecules WHERE id=?'),
  getMolByFormula:db.prepare('SELECT id,name_ru,name_lat,category,description,topic_tags FROM bio_molecules WHERE formula=? LIMIT 1'),
  getReactions:   db.prepare('SELECT * FROM bio_reactions ORDER BY name_ru'),
  getChallenges:  db.prepare('SELECT * FROM bio_challenges ORDER BY difficulty,order_n'),
  getChallenge:   db.prepare('SELECT * FROM bio_challenges WHERE id=?'),
  checkDone:      db.prepare('SELECT 1 FROM bio_user_challenges WHERE user_id=? AND challenge_id=?'),
  markDone:       db.prepare('INSERT OR IGNORE INTO bio_user_challenges (user_id,challenge_id) VALUES (?,?)'),
  getDoneIds:     db.prepare('SELECT challenge_id FROM bio_user_challenges WHERE user_id=?'),
  getSaved:       db.prepare('SELECT bm.*, bmo.name_ru AS mol_name FROM bio_user_molecules bm LEFT JOIN bio_molecules bmo ON bmo.id=bm.molecule_id WHERE bm.user_id=? ORDER BY bm.created_at DESC'),
  saveMol:        db.prepare('INSERT INTO bio_user_molecules (user_id,molecule_id,name,formula,atoms_json,bonds_json) VALUES (?,?,?,?,?,?)'),
  deleteSaved:    db.prepare('DELETE FROM bio_user_molecules WHERE id=? AND user_id=?'),
};

/* ── GET /api/biochem/elements ───────────────────────────────────────── */
function getElements(_req, res) {
  res.json(stmts.getElements.all());
}

/* ── GET /api/biochem/molecules ──────────────────────────────────────── */
function getMolecules(req, res) {
  const { cat, q } = req.query;
  let rows;
  if (q) {
    const like = `%${q}%`;
    rows = stmts.getMolSearch.all(like, like);
  } else if (cat) {
    rows = stmts.getMolCat.all(cat);
  } else {
    rows = stmts.getMolecules.all();
  }
  rows = rows.map(r => ({
    ...r,
    topic_tags: tryParse(r.topic_tags, []),
    atoms_json: tryParse(r.atoms_json, []),
    bonds_json: tryParse(r.bonds_json, []),
  }));
  res.json(rows);
}

/* ── GET /api/biochem/molecules/:id ─────────────────────────────────── */
function getMolecule(req, res) {
  const mol = stmts.getMolById.get(req.params.id);
  if (!mol) return res.status(404).json({ error: 'Not found' });
  res.json({
    ...mol,
    atoms_json:  tryParse(mol.atoms_json, []),
    bonds_json:  tryParse(mol.bonds_json, []),
    topic_tags:  tryParse(mol.topic_tags, []),
  });
}

/* ── POST /api/biochem/validate ─────────────────────────────────────── */
function validate(req, res) {
  const { atoms, bonds } = req.body;
  if (!Array.isArray(atoms) || !Array.isArray(bonds))
    return res.status(400).json({ error: 'atoms[] and bonds[] required' });
  if (atoms.length === 0) return res.json({ valid: false, formula: '', issues: [] });

  const formula = hillFormula(atoms);
  const issues  = valencyIssues(atoms, bonds);
  const valid   = issues.length === 0;

  const known = valid ? stmts.getMolByFormula.get(formula) : null;
  res.json({ valid, formula, issues, known: known || null });
}

/* ── GET /api/biochem/reactions ─────────────────────────────────────── */
function getReactions(_req, res) {
  const rows = stmts.getReactions.all().map(r => ({
    ...r,
    reactant_ids: tryParse(r.reactant_ids, []),
    product_ids:  tryParse(r.product_ids, []),
    topic_tags:   tryParse(r.topic_tags, []),
  }));
  res.json(rows);
}

/* ── GET /api/biochem/challenges ────────────────────────────────────── */
function getChallenges(req, res) {
  const challenges = stmts.getChallenges.all();
  const doneSet = new Set(stmts.getDoneIds.all(req.user.id).map(r => r.challenge_id));
  res.json(challenges.map(c => ({
    ...c,
    data_json: tryParse(c.data_json, null),
    done: doneSet.has(c.id),
  })));
}

/* ── POST /api/biochem/challenges/:id/solve ─────────────────────────── */
function solveChallenge(req, res) {
  const challenge = stmts.getChallenge.get(req.params.id);
  if (!challenge) return res.status(404).json({ error: 'Challenge not found' });

  if (stmts.checkDone.get(req.user.id, challenge.id))
    return res.status(400).json({ error: 'already_completed' });

  const type = challenge.type || 'build';

  /* ── identify: shown structure → pick name ── */
  if (type === 'identify') {
    const { answer } = req.body;
    if (typeof answer !== 'string' || !answer)
      return res.status(400).json({ error: 'answer required' });
    const mol = stmts.getMolByFormula.get(challenge.target_formula);
    if (!mol || answer !== mol.name_ru)
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── formula: shown name → pick formula ── */
  if (type === 'formula') {
    const { answer } = req.body;
    if (typeof answer !== 'string' || !answer)
      return res.status(400).json({ error: 'answer required' });
    if (answer !== challenge.target_formula)
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── classify: shown molecule → pick class ── */
  if (type === 'classify') {
    const { answer } = req.body;
    if (typeof answer !== 'string' || !answer)
      return res.status(400).json({ error: 'answer required' });
    const data = tryParse(challenge.data_json, {});
    if (answer !== data.answer)
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── complete: shown partial reaction → pick missing component ── */
  if (type === 'complete') {
    const { answer } = req.body;
    if (typeof answer !== 'string' || !answer)
      return res.status(400).json({ error: 'answer required' });
    const data = tryParse(challenge.data_json, {});
    if (answer !== data.answer)
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── balance: fill coefficients to balance equation ── */
  if (type === 'balance') {
    const { coefficients } = req.body;
    const data = tryParse(challenge.data_json, {});
    const expected = data.coefficients || [];
    if (!Array.isArray(coefficients) ||
        coefficients.length !== expected.length ||
        !coefficients.every((c, i) => parseInt(c) === expected[i]))
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── match: pair left items with right items ── */
  if (type === 'match') {
    const { pairs } = req.body;
    const data = tryParse(challenge.data_json, {});
    const answerMap = new Map((data.pairs || []).map(p => [p.left, p.right]));
    if (!Array.isArray(pairs) ||
        pairs.length !== answerMap.size ||
        !pairs.every(p => answerMap.get(p.left) === p.right))
      return res.status(400).json({ error: 'wrong_answer' });
    stmts.markDone.run(req.user.id, challenge.id);
    awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
    return res.json({ ok: true, xp: challenge.xp_reward });
  }

  /* ── build (default): draw the molecule ── */
  const { atoms, bonds } = req.body;
  if (!Array.isArray(atoms) || !Array.isArray(bonds))
    return res.status(400).json({ error: 'atoms[] and bonds[] required' });

  const formula = hillFormula(atoms);
  if (formula !== challenge.target_formula)
    return res.status(400).json({ error: 'wrong_formula', submitted: formula, expected: challenge.target_formula });

  const issues = valencyIssues(atoms, bonds);
  if (issues.length > 0)
    return res.status(400).json({ error: 'valency_error', issues });

  stmts.markDone.run(req.user.id, challenge.id);
  awardXP(req.user.id, challenge.xp_reward, `biochem_challenge:${challenge.id}`);
  res.json({ ok: true, xp: challenge.xp_reward });
}

/* ── GET /api/biochem/saved ─────────────────────────────────────────── */
function getSaved(req, res) {
  const rows = stmts.getSaved.all(req.user.id).map(r => ({
    ...r,
    atoms_json: tryParse(r.atoms_json, []),
    bonds_json: tryParse(r.bonds_json, []),
  }));
  res.json(rows);
}

/* ── POST /api/biochem/saved ────────────────────────────────────────── */
function saveMolecule(req, res) {
  const { atoms, bonds, name } = req.body;
  if (!Array.isArray(atoms) || !Array.isArray(bonds) || atoms.length === 0)
    return res.status(400).json({ error: 'atoms[] and bonds[] required' });

  const formula = hillFormula(atoms);
  const known   = stmts.getMolByFormula.get(formula);
  const id = stmts.saveMol.run(
    req.user.id,
    known?.id ?? null,
    name?.trim() || null,
    formula,
    JSON.stringify(atoms),
    JSON.stringify(bonds),
  ).lastInsertRowid;
  res.status(201).json({ id, formula, known: known || null });
}

/* ── DELETE /api/biochem/saved/:id ──────────────────────────────────── */
function deleteSaved(req, res) {
  const info = stmts.deleteSaved.run(req.params.id, req.user.id);
  if (info.changes === 0) return res.status(404).json({ error: 'Not found' });
  res.json({ ok: true });
}

/* ── util ────────────────────────────────────────────────────────────── */
function tryParse(v, fallback) {
  if (!v) return fallback;
  try { return JSON.parse(v); } catch { return fallback; }
}

module.exports = {
  getElements, getMolecules, getMolecule, validate,
  getReactions, getChallenges, solveChallenge,
  getSaved, saveMolecule, deleteSaved,
};
