"""Token store — SQLite-backed token management."""
from __future__ import annotations

import secrets
import sqlite3
import time
import logging
from pathlib import Path
from typing import List, Optional

from mesh.security.auth import generate_token

logger = logging.getLogger(__name__)


def _mask_token(token_id: str) -> str:
    """Mask a token ID/value for safe logging — show only first 8 chars."""
    if not token_id:
        return "<empty>"
    if len(token_id) <= 8:
        return token_id[:4] + "****"
    return token_id[:8] + "****"


class TokenStore:
    """SQLite-backed store for registration and API tokens."""

    def __init__(self, db_path: str = "data/mesh_tokens.db"):
        self.db_path = Path(db_path)
        self.db_path.parent.mkdir(parents=True, exist_ok=True)
        self._init_db()

    def _init_db(self):
        with self._conn() as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS tokens (
                    token_id TEXT PRIMARY KEY,
                    token_type TEXT NOT NULL,
                    node_id TEXT,
                    role TEXT DEFAULT 'node',
                    token_value TEXT NOT NULL,
                    created_at REAL NOT NULL,
                    expires_at REAL,
                    used INTEGER DEFAULT 0,
                    revoked INTEGER DEFAULT 0
                )
            """)

    def _conn(self) -> sqlite3.Connection:
        conn = sqlite3.connect(str(self.db_path))
        conn.row_factory = sqlite3.Row
        return conn

    def create_registration_token(self, secret: str, ttl_hours: int = 1) -> str:
        """Create a one-time-use registration token."""
        token_id = secrets.token_hex(8)
        token_value = generate_token(
            node_id="__registration__",
            secret=secret,
            ttl_hours=ttl_hours,
            role="registration",
            extra={"token_id": token_id, "one_time": True},
        )
        now = time.time()
        with self._conn() as conn:
            conn.execute(
                "INSERT INTO tokens (token_id, token_type, token_value, created_at, expires_at) "
                "VALUES (?, 'registration', ?, ?, ?)",
                (token_id, token_value, now, now + ttl_hours * 3600),
            )
        logger.info("Created registration token: %s", _mask_token(token_id))
        return token_value

    def create_api_token(
        self,
        node_id: str,
        role: str,
        secret: str,
        ttl_hours: int = 24 * 365,
    ) -> str:
        """Create a long-lived API token for a registered node."""
        token_id = secrets.token_hex(8)
        token_value = generate_token(
            node_id=node_id,
            secret=secret,
            ttl_hours=ttl_hours,
            role=role,
            extra={"token_id": token_id},
        )
        now = time.time()
        with self._conn() as conn:
            conn.execute(
                "INSERT INTO tokens (token_id, token_type, node_id, role, token_value, "
                "created_at, expires_at) VALUES (?, 'api', ?, ?, ?, ?, ?)",
                (token_id, node_id, role, token_value, now, now + ttl_hours * 3600),
            )
        logger.info("Created API token for node %s (role=%s): %s",
                     node_id, role, _mask_token(token_id))
        return token_value

    def consume_registration_token(self, token_value: str) -> bool:
        """Mark a registration token as used. Returns True if valid and unused."""
        with self._conn() as conn:
            row = conn.execute(
                "SELECT token_id, used, revoked, expires_at FROM tokens "
                "WHERE token_value = ? AND token_type = 'registration'",
                (token_value,),
            ).fetchone()
            if row is None:
                return False
            if row["used"] or row["revoked"]:
                return False
            if row["expires_at"] and time.time() > row["expires_at"]:
                return False
            conn.execute(
                "UPDATE tokens SET used = 1 WHERE token_id = ?",
                (row["token_id"],),
            )
            return True

    def revoke_token(self, token_id: str) -> bool:
        """Revoke a token by its ID."""
        with self._conn() as conn:
            cursor = conn.execute(
                "UPDATE tokens SET revoked = 1 WHERE token_id = ?",
                (token_id,),
            )
            revoked = cursor.rowcount > 0
            if revoked:
                logger.info("Revoked token: %s", _mask_token(token_id))
            return revoked

    def is_revoked(self, token_value: str) -> bool:
        """Check if a token has been revoked."""
        with self._conn() as conn:
            row = conn.execute(
                "SELECT revoked FROM tokens WHERE token_value = ?",
                (token_value,),
            ).fetchone()
            if row is None:
                return False  # Unknown tokens aren't "revoked" — they just fail sig check
            return bool(row["revoked"])

    def list_active_tokens(self) -> list:
        """List all active (non-revoked, non-expired) tokens."""
        now = time.time()
        with self._conn() as conn:
            rows = conn.execute(
                "SELECT token_id, token_type, node_id, role, created_at, expires_at "
                "FROM tokens WHERE revoked = 0 AND (expires_at IS NULL OR expires_at > ?) "
                "ORDER BY created_at DESC",
                (now,),
            ).fetchall()
            return [dict(r) for r in rows]
