from __future__ import print_function
import json
import os
import sqlite3
import threading
from .models import Alert
from .util import isoformat

SCHEMA = """
CREATE TABLE IF NOT EXISTS active_alerts (
  alert_id TEXT PRIMARY KEY,
  payload TEXT NOT NULL,
  fingerprint TEXT NOT NULL,
  first_seen TEXT NOT NULL,
  last_seen TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS metadata (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS action_states (
  mapping_key TEXT PRIMARY KEY,
  active INTEGER NOT NULL,
  updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS event_log (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  created_at TEXT NOT NULL,
  kind TEXT NOT NULL,
  alert_id TEXT,
  event TEXT,
  detail TEXT
);
CREATE INDEX IF NOT EXISTS idx_event_log_created ON event_log(created_at);
"""


class StateStore(object):
    def __init__(self, path):
        self.path = path
        self.lock = threading.RLock()
        directory = os.path.dirname(path)
        if directory and not os.path.isdir(directory):
            os.makedirs(directory)
        self.conn = sqlite3.connect(path, timeout=30, check_same_thread=False)
        self.conn.row_factory = sqlite3.Row
        with self.conn:
            self.conn.executescript(SCHEMA)
            self.conn.execute("PRAGMA journal_mode=WAL")
            self.conn.execute("PRAGMA synchronous=NORMAL")

    def get_alerts(self):
        with self.lock:
            rows = self.conn.execute("SELECT payload FROM active_alerts").fetchall()
        result = {}
        for row in rows:
            data = json.loads(row["payload"])
            alert = Alert.from_dict(data)
            result[alert.id] = alert
        return result

    def replace_alerts(self, alerts):
        now = isoformat()
        existing = {}
        with self.lock:
            for row in self.conn.execute("SELECT alert_id, first_seen, fingerprint FROM active_alerts"):
                existing[row["alert_id"]] = dict(row)
            incoming = set(alerts.keys())
            current = set(existing.keys())
            changed = incoming != current
            if not changed:
                for alert_id, alert in alerts.items():
                    if existing[alert_id]["fingerprint"] != alert.fingerprint:
                        changed = True
                        break
            if not changed:
                return False
            with self.conn:
                for removed in current - incoming:
                    self.conn.execute("DELETE FROM active_alerts WHERE alert_id=?", (removed,))
                for alert_id, alert in alerts.items():
                    first_seen = existing.get(alert_id, {}).get("first_seen", now)
                    self.conn.execute(
                        "INSERT OR REPLACE INTO active_alerts(alert_id,payload,fingerprint,first_seen,last_seen) VALUES(?,?,?,?,?)",
                        (alert_id, json.dumps(alert.to_dict(), sort_keys=True), alert.fingerprint, first_seen, now),
                    )
            return True

    def get_meta(self, key, default=None):
        with self.lock:
            row = self.conn.execute("SELECT value FROM metadata WHERE key=?", (key,)).fetchone()
        if not row:
            return default
        try:
            return json.loads(row["value"])
        except Exception:
            return row["value"]

    def set_meta(self, key, value):
        encoded = json.dumps(value)
        with self.lock:
            row = self.conn.execute("SELECT value FROM metadata WHERE key=?", (key,)).fetchone()
            if row and row["value"] == encoded:
                return False
            with self.conn:
                self.conn.execute("INSERT OR REPLACE INTO metadata(key,value) VALUES(?,?)", (key, encoded))
        return True

    def get_action_state(self, key):
        with self.lock:
            row = self.conn.execute("SELECT active FROM action_states WHERE mapping_key=?", (key,)).fetchone()
        return bool(row["active"]) if row else False

    def set_action_state(self, key, active):
        encoded = 1 if active else 0
        with self.lock:
            row = self.conn.execute("SELECT active FROM action_states WHERE mapping_key=?", (key,)).fetchone()
            if row and int(row["active"]) == encoded:
                return False
            with self.conn:
                self.conn.execute(
                    "INSERT OR REPLACE INTO action_states(mapping_key,active,updated_at) VALUES(?,?,?)",
                    (key, encoded, isoformat()),
                )
        return True

    def log(self, kind, alert=None, detail=""):
        alert_id = alert.id if alert else None
        event = alert.event if alert else None
        with self.lock, self.conn:
            self.conn.execute(
                "INSERT INTO event_log(created_at,kind,alert_id,event,detail) VALUES(?,?,?,?,?)",
                (isoformat(), kind, alert_id, event, str(detail or "")),
            )
            self.conn.execute("DELETE FROM event_log WHERE id NOT IN (SELECT id FROM event_log ORDER BY id DESC LIMIT 1000)")

    def recent_events(self, limit=50):
        with self.lock:
            rows = self.conn.execute("SELECT * FROM event_log ORDER BY id DESC LIMIT ?", (int(limit),)).fetchall()
        return [dict(row) for row in rows]

    def close(self):
        with self.lock:
            if self.conn is not None:
                self.conn.close()
                self.conn = None
