"""ledger.py -- the miner's own atomic, resumable trial ledger (sqlite). One row per cell per stage, written in one transaction per chunk BEFORE any
selection logic reads the results. The global project ledger (quantlab/artifacts/global_trial_ledger.json) is never touched here; run_miner appends ONE
closing event with n = distinct cells evaluated on real data (backup first, as _new_ledger_append.py).
"""
from __future__ import annotations

import json
import os
import sqlite3
import time

import config as C

PATH = os.path.join(C.OUT, "ledger.sqlite")


class Ledger:
    def __init__(self, path: str = PATH):
        self.path = path
        self.cx = sqlite3.connect(path, timeout=60, isolation_level=None)
        self.cx.execute("PRAGMA journal_mode=WAL")
        self.cx.execute("CREATE TABLE IF NOT EXISTS runs(run_id TEXT PRIMARY KEY, created_utc TEXT, prereg_sha TEXT, note TEXT)")
        self.cx.execute("CREATE TABLE IF NOT EXISTS cells(run_id TEXT, stage TEXT, cell_id TEXT, n REAL, mean REAL, t_gate REAL, payload TEXT, "
                        "written_utc TEXT, PRIMARY KEY(run_id, stage, cell_id))")
        self.cx.execute("CREATE TABLE IF NOT EXISTS chunks(run_id TEXT, stage TEXT, chunk TEXT, written_utc TEXT, info TEXT, PRIMARY KEY(run_id, stage, chunk))")
        self.cx.execute("CREATE TABLE IF NOT EXISTS events(ts TEXT, run_id TEXT, kind TEXT, payload TEXT)")

    def log_run(self, run_id: str, prereg_sha: str, note: str = ""):
        self.cx.execute("INSERT OR IGNORE INTO runs VALUES(?,?,?,?)", (run_id, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), prereg_sha, note))

    def chunk_done(self, run_id: str, stage: str, chunk: str) -> bool:
        return self.cx.execute("SELECT 1 FROM chunks WHERE run_id=? AND stage=? AND chunk=?", (run_id, stage, chunk)).fetchone() is not None

    def write_chunk(self, run_id: str, stage: str, chunk: str, rows: list, info: dict | None = None):
        """rows: [(cell_id, n, mean, t_gate, payload_dict)]; atomic with the chunk marker."""
        ts = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
        self.cx.execute("BEGIN")
        try:
            self.cx.executemany("INSERT OR REPLACE INTO cells VALUES(?,?,?,?,?,?,?,?)",
                                [(run_id, stage, cid, n, m, t, json.dumps(p, default=float), ts) for cid, n, m, t, p in rows])
            self.cx.execute("INSERT OR REPLACE INTO chunks VALUES(?,?,?,?,?)", (run_id, stage, chunk, ts, json.dumps(info or {}, default=float)))
            self.cx.execute("COMMIT")
        except Exception:
            self.cx.execute("ROLLBACK")
            raise

    def event(self, run_id: str, kind: str, payload: dict):
        self.cx.execute("INSERT INTO events VALUES(?,?,?,?)", (time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), run_id, kind, json.dumps(payload, default=str)))

    def n_cells(self, run_id: str, stage: str) -> int:
        return self.cx.execute("SELECT COUNT(*) FROM cells WHERE run_id=? AND stage=?", (run_id, stage)).fetchone()[0]
