import json, random, os
OUT = os.path.join(os.path.dirname(__file__), "..", "question_bank")

class Bank:
    """Collects de-duplicated MCQs for one subject. Answer position is shuffled with a fixed seed."""
    def __init__(self, subject, seed=1):
        self.subject, self.rng, self.qs, self.seen = subject, random.Random(seed), [], set()
    def add(self, q, correct, wrongs, topic="General", diff="Medium", same_stem=False):
        correct = str(correct); wr = []
        for w in wrongs:
            w = str(w)
            if w != correct and w not in wr: wr.append(w)
        opts = wr[:3] + [correct]
        key = q if not same_stem else q + '||' + '|'.join(sorted(opts))
        if len(wr) < 3 or key in self.seen or (same_stem and q in self.seen and False): return False
        self.rng.shuffle(opts)
        self.seen.add(key); self.qs.append({"q": q, "o": opts, "a": opts.index(correct), "t": topic, "d": diff}); return True
    def fill(self, gen, n, tries=40):
        """Call generator gen(rng)->bool until n questions were added (or attempts run out)."""
        got = 0; t = 0
        while got < n and t < n * tries:
            t += 1
            if gen(self.rng): got += 1
        return got
    def pairs(self, facts, qfwd=None, qrev=None, topic="General", diff="Easy", k=None):
        """facts: list of (key, value). qfwd uses {k}; qrev uses {v}. Distractors come from other values."""
        vals = [v for _, v in facts]; keys = [k_ for k_, _ in facts]
        for key, val in facts:
            if qfwd:
                pool = [v for v in vals if v != val]; self.rng.shuffle(pool)
                self.add(qfwd.format(k=key), val, pool[:3], topic, diff)
            if qrev:
                pool = [x for x in keys if x != key]; self.rng.shuffle(pool)
                self.add(qrev.format(v=val), key, pool[:3], topic, diff)
    def save(self):
        os.makedirs(OUT, exist_ok=True)
        with open(os.path.join(OUT, self.subject + ".json"), "w") as f:
            json.dump({"subject": self.subject, "questions": self.qs}, f, ensure_ascii=False, separators=(",", ":"))
        return len(self.qs)

def near(rng, c, extra=(), step=None, integer=True):
    """Distractor numbers around c (+ typical-error values in extra)."""
    out = [x for x in extra]
    s = step or max(1, abs(c) // 10 if integer else 1)
    for d in (1, -1, 2, -2, 3, -3, 5, -5):
        out.append(c + d * s)
    rng.shuffle(out)
    return [x for x in out if x != c]

def _extend(self):
    p=os.path.join(OUT,self.subject+".json")
    if os.path.exists(p):
        self.qs=json.load(open(p))["questions"]; self.seen={q["q"] for q in self.qs}|{q["q"]+'||'+'|'.join(sorted(q["o"])) for q in self.qs}
    return self
Bank.extend=_extend
def _pairs_text(self, text, qfwd=None, qrev=None, topic="General", diff="Easy"):
    facts=[tuple(x.strip() for x in ln.split("|",1)) for ln in text.strip().splitlines() if "|" in ln]
    self.pairs(facts,qfwd,qrev,topic,diff)
Bank.ptext=_pairs_text
def _qa(self, text, topic="General", diff="Medium"):
    for ln in text.strip().splitlines():
        p=[x.strip() for x in ln.split("|")]
        if len(p)>=5: self.add(p[0],p[1],p[2:5],topic,diff,same_stem=True)
Bank.qa=_qa
