"""
License storage — SQLite database for license keys, activations, and revocations.
"""
from __future__ import annotations

import sqlite3
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from config import DB_PATH, TIERS, PRODUCT_ID

def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()

def _connect() -> sqlite3.Connection:
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    return conn

def init_db() -> None:
    conn = _connect()
    conn.executescript("""
        CREATE TABLE IF NOT EXISTS licenses (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            license_key TEXT UNIQUE NOT NULL,
            tier TEXT NOT NULL DEFAULT 'trial',
            customer_email TEXT,
            max_machines INTEGER NOT NULL DEFAULT 1,
            issued_at TEXT NOT NULL,
            expires_at TEXT,
            revoked INTEGER NOT NULL DEFAULT 0,
            revoked_at TEXT,
            notes TEXT
        );
        CREATE TABLE IF NOT EXISTS activations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            license_key TEXT NOT NULL,
            hardware_id TEXT NOT NULL,
            machine_name TEXT,
            activated_at TEXT NOT NULL,
            last_seen_at TEXT NOT NULL,
            deactivated INTEGER NOT NULL DEFAULT 0,
            FOREIGN KEY (license_key) REFERENCES licenses(license_key),
            UNIQUE(license_key, hardware_id)
        );
        CREATE TABLE IF NOT EXISTS audit_log (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            ts TEXT NOT NULL,
            action TEXT NOT NULL,
            license_key TEXT,
            detail TEXT
        );
        CREATE INDEX IF NOT EXISTS idx_licenses_key ON licenses(license_key);
        CREATE INDEX IF NOT EXISTS idx_activations_key ON activations(license_key);
        CREATE INDEX IF NOT EXISTS idx_audit_ts ON audit_log(ts);
    """)
    conn.commit()
    conn.close()

# ─── CRUD: Licenses ─────────────────────────────────────────────────

def create_license(license_key: str, tier: str, customer_email: str = "",
                   max_machines: int = 0, expires_at: str = "", notes: str = "") -> dict[str, Any]:
    tier_config = TIERS.get(tier, TIERS["trial"])
    if max_machines <= 0:
        max_machines = tier_config["max_machines"]
    if not expires_at:
        expires_ts = int(time.time()) + tier_config["max_days"] * 86400
        expires_at = datetime.fromtimestamp(expires_ts, tz=timezone.utc).isoformat()

    conn = _connect()
    conn.execute(
        "INSERT INTO licenses (license_key, tier, customer_email, max_machines, issued_at, expires_at, notes) "
        "VALUES (?, ?, ?, ?, ?, ?, ?)",
        (license_key, tier, customer_email, max_machines, _now_iso(), expires_at, notes),
    )
    conn.commit()
    row = conn.execute("SELECT * FROM licenses WHERE license_key = ?", (license_key,)).fetchone()
    conn.close()
    return dict(row) if row else {}

def get_license(license_key: str) -> dict[str, Any] | None:
    conn = _connect()
    row = conn.execute("SELECT * FROM licenses WHERE license_key = ?", (license_key,)).fetchone()
    conn.close()
    return dict(row) if row else None

def revoke_license(license_key: str, reason: str = "") -> bool:
    conn = _connect()
    conn.execute(
        "UPDATE licenses SET revoked = 1, revoked_at = ?, notes = notes || ? WHERE license_key = ?",
        (_now_iso(), f" | REVOKED: {reason}" if reason else "", license_key),
    )
    conn.execute(
        "UPDATE activations SET deactivated = 1 WHERE license_key = ?",
        (license_key,),
    )
    conn.commit()
    affected = conn.total_changes
    conn.close()
    return affected > 0

def list_licenses(include_revoked: bool = False) -> list[dict[str, Any]]:
    conn = _connect()
    if include_revoked:
        rows = conn.execute("SELECT * FROM licenses ORDER BY issued_at DESC").fetchall()
    else:
        rows = conn.execute(
            "SELECT * FROM licenses WHERE revoked = 0 ORDER BY issued_at DESC"
        ).fetchall()
    conn.close()
    return [dict(r) for r in rows]

# ─── Activations ────────────────────────────────────────────────────

def activate(license_key: str, hardware_id: str, machine_name: str = "") -> dict[str, Any]:
    """Activate a license on a machine. Returns activation result."""
    lic = get_license(license_key)
    if not lic:
        return {"ok": False, "error": "LICENSE_NOT_FOUND", "message": "License key not found."}
    if lic["revoked"]:
        return {"ok": False, "error": "LICENSE_REVOKED", "message": "This license has been revoked."}
    if lic["expires_at"]:
        try:
            exp = datetime.fromisoformat(lic["expires_at"])
            if datetime.now(timezone.utc) > exp:
                return {"ok": False, "error": "LICENSE_EXPIRED", "message": f"License expired on {lic['expires_at']}."}
        except ValueError:
            pass

    conn = _connect()
    # Count existing activations for this license
    active_count = conn.execute(
        "SELECT COUNT(*) FROM activations WHERE license_key = ? AND deactivated = 0",
        (license_key,),
    ).fetchone()[0]

    # Check if this hardware already activated
    existing = conn.execute(
        "SELECT * FROM activations WHERE license_key = ? AND hardware_id = ? AND deactivated = 0",
        (license_key, hardware_id),
    ).fetchone()

    if existing:
        # Re-activation — update last_seen
        conn.execute(
            "UPDATE activations SET last_seen_at = ?, machine_name = ? WHERE id = ?",
            (_now_iso(), machine_name, existing["id"]),
        )
        conn.commit()
        conn.close()
        return {
            "ok": True,
            "action": "reactivated",
            "license": _license_payload(lic),
            "hardware_id": hardware_id,
        }

    if active_count >= lic["max_machines"]:
        conn.close()
        return {
            "ok": False,
            "error": "MACHINE_LIMIT_REACHED",
            "message": f"License limited to {lic['max_machines']} machine(s). Deactivate an existing machine first.",
        }

    now = _now_iso()
    conn.execute(
        "INSERT INTO activations (license_key, hardware_id, machine_name, activated_at, last_seen_at) "
        "VALUES (?, ?, ?, ?, ?)",
        (license_key, hardware_id, machine_name, now, now),
    )
    conn.commit()
    conn.close()
    return {
        "ok": True,
        "action": "activated",
        "license": _license_payload(lic),
        "hardware_id": hardware_id,
    }

def check_online(license_key: str, hardware_id: str) -> dict[str, Any]:
    """Periodic online check — validates license is still active and not revoked."""
    lic = get_license(license_key)
    if not lic:
        return {"ok": False, "error": "LICENSE_NOT_FOUND"}
    if lic["revoked"]:
        return {"ok": False, "error": "LICENSE_REVOKED"}
    if lic["expires_at"]:
        try:
            exp = datetime.fromisoformat(lic["expires_at"])
            if datetime.now(timezone.utc) > exp:
                return {"ok": False, "error": "LICENSE_EXPIRED"}
        except ValueError:
            pass

    # Update last_seen
    conn = _connect()
    conn.execute(
        "UPDATE activations SET last_seen_at = ? WHERE license_key = ? AND hardware_id = ? AND deactivated = 0",
        (_now_iso(), license_key, hardware_id),
    )
    conn.commit()
    conn.close()
    return {"ok": True, "license": _license_payload(lic)}

def _license_payload(lic: dict[str, Any]) -> dict[str, Any]:
    """Build the license payload for client-side use."""
    return {
        "product": PRODUCT_ID,
        "version": 1,
        "tier": lic["tier"],
        "customer_email": lic.get("customer_email", ""),
        "issued_at": lic["issued_at"],
        "expires_at": lic.get("expires_at", ""),
        "features": TIERS.get(lic["tier"], TIERS["trial"])["features"],
    }
