"""
SQLite database utilities for persistent settings storage.
"""

from __future__ import annotations

import json
import logging
import sqlite3
import threading
from contextlib import contextmanager
from datetime import datetime
from pathlib import Path
from typing import Any

logger = logging.getLogger('intercept.database')

# Database file location
DB_DIR = Path(__file__).parent.parent / 'instance'
DB_PATH = DB_DIR / 'intercept.db'

# Thread-local storage for connections
_local = threading.local()


def get_db_path() -> Path:
    """Get the database file path, creating directory if needed."""
    DB_DIR.mkdir(parents=True, exist_ok=True)
    return DB_PATH


def get_connection() -> sqlite3.Connection:
    """Get a thread-local database connection."""
    if not hasattr(_local, 'connection') or _local.connection is None:
        db_path = get_db_path()
        _local.connection = sqlite3.connect(str(db_path), check_same_thread=False)
        _local.connection.row_factory = sqlite3.Row
        # Enable foreign keys
        _local.connection.execute('PRAGMA foreign_keys = ON')
    return _local.connection


@contextmanager
def get_db():
    """Context manager for database operations."""
    conn = get_connection()
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise


def init_db() -> None:
    """Initialize the database schema."""
    db_path = get_db_path()
    logger.info(f"Initializing database at {db_path}")

    with get_db() as conn:
        # Settings table for key-value storage
        conn.execute('''
            CREATE TABLE IF NOT EXISTS settings (
                key TEXT PRIMARY KEY,
                value TEXT NOT NULL,
                value_type TEXT DEFAULT 'string',
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')

        # Signal history table for graphs
        conn.execute('''
            CREATE TABLE IF NOT EXISTS signal_history (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                mode TEXT NOT NULL,
                device_id TEXT NOT NULL,
                signal_strength REAL,
                timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                metadata TEXT
            )
        ''')

        # Create index for faster queries
        conn.execute('''
            CREATE INDEX IF NOT EXISTS idx_signal_history_mode_device
            ON signal_history(mode, device_id, timestamp)
        ''')

        # Device correlation table
        conn.execute('''
            CREATE TABLE IF NOT EXISTS device_correlations (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                wifi_mac TEXT,
                bt_mac TEXT,
                confidence REAL,
                first_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                last_seen TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                metadata TEXT,
                UNIQUE(wifi_mac, bt_mac)
            )
        ''')

        # =====================================================================
        # TSCM (Technical Surveillance Countermeasures) Tables
        # =====================================================================

        # TSCM Baselines - Environment snapshots for comparison
        conn.execute('''
            CREATE TABLE IF NOT EXISTS tscm_baselines (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                location TEXT,
                description TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                wifi_networks TEXT,
                bt_devices TEXT,
                rf_frequencies TEXT,
                gps_coords TEXT,
                is_active BOOLEAN DEFAULT 0
            )
        ''')

        # TSCM Sweeps - Individual sweep sessions
        conn.execute('''
            CREATE TABLE IF NOT EXISTS tscm_sweeps (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                baseline_id INTEGER,
                started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                completed_at TIMESTAMP,
                status TEXT DEFAULT 'running',
                sweep_type TEXT,
                wifi_enabled BOOLEAN DEFAULT 1,
                bt_enabled BOOLEAN DEFAULT 1,
                rf_enabled BOOLEAN DEFAULT 1,
                results TEXT,
                anomalies TEXT,
                threats_found INTEGER DEFAULT 0,
                FOREIGN KEY (baseline_id) REFERENCES tscm_baselines(id)
            )
        ''')

        # TSCM Threats - Detected threats/anomalies
        conn.execute('''
            CREATE TABLE IF NOT EXISTS tscm_threats (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                sweep_id INTEGER,
                detected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                threat_type TEXT NOT NULL,
                severity TEXT DEFAULT 'medium',
                source TEXT,
                identifier TEXT,
                name TEXT,
                signal_strength INTEGER,
                frequency REAL,
                details TEXT,
                acknowledged BOOLEAN DEFAULT 0,
                notes TEXT,
                gps_coords TEXT,
                FOREIGN KEY (sweep_id) REFERENCES tscm_sweeps(id)
            )
        ''')

        # TSCM Scheduled Sweeps
        conn.execute('''
            CREATE TABLE IF NOT EXISTS tscm_schedules (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                baseline_id INTEGER,
                zone_name TEXT,
                cron_expression TEXT,
                sweep_type TEXT DEFAULT 'standard',
                enabled BOOLEAN DEFAULT 1,
                last_run TIMESTAMP,
                next_run TIMESTAMP,
                notify_on_threat BOOLEAN DEFAULT 1,
                notify_email TEXT,
                FOREIGN KEY (baseline_id) REFERENCES tscm_baselines(id)
            )
        ''')

        # TSCM indexes for performance
        conn.execute('''
            CREATE INDEX IF NOT EXISTS idx_tscm_threats_sweep
            ON tscm_threats(sweep_id)
        ''')

        conn.execute('''
            CREATE INDEX IF NOT EXISTS idx_tscm_threats_severity
            ON tscm_threats(severity, detected_at)
        ''')

        conn.execute('''
            CREATE INDEX IF NOT EXISTS idx_tscm_sweeps_baseline
            ON tscm_sweeps(baseline_id)
        ''')

        logger.info("Database initialized successfully")


def close_db() -> None:
    """Close the thread-local database connection."""
    if hasattr(_local, 'connection') and _local.connection is not None:
        _local.connection.close()
        _local.connection = None


# =============================================================================
# Settings Functions
# =============================================================================

def get_setting(key: str, default: Any = None) -> Any:
    """
    Get a setting value by key.

    Args:
        key: Setting key
        default: Default value if not found

    Returns:
        Setting value (auto-converted from JSON for complex types)
    """
    with get_db() as conn:
        cursor = conn.execute(
            'SELECT value, value_type FROM settings WHERE key = ?',
            (key,)
        )
        row = cursor.fetchone()

        if row is None:
            return default

        value, value_type = row['value'], row['value_type']

        # Convert based on type
        if value_type == 'json':
            try:
                return json.loads(value)
            except json.JSONDecodeError:
                return default
        elif value_type == 'int':
            return int(value)
        elif value_type == 'float':
            return float(value)
        elif value_type == 'bool':
            return value.lower() in ('true', '1', 'yes')
        else:
            return value


def set_setting(key: str, value: Any) -> None:
    """
    Set a setting value.

    Args:
        key: Setting key
        value: Setting value (will be JSON-encoded for complex types)
    """
    # Determine value type and string representation
    if isinstance(value, bool):
        value_type = 'bool'
        str_value = 'true' if value else 'false'
    elif isinstance(value, int):
        value_type = 'int'
        str_value = str(value)
    elif isinstance(value, float):
        value_type = 'float'
        str_value = str(value)
    elif isinstance(value, (dict, list)):
        value_type = 'json'
        str_value = json.dumps(value)
    else:
        value_type = 'string'
        str_value = str(value)

    with get_db() as conn:
        conn.execute('''
            INSERT INTO settings (key, value, value_type, updated_at)
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
            ON CONFLICT(key) DO UPDATE SET
                value = excluded.value,
                value_type = excluded.value_type,
                updated_at = CURRENT_TIMESTAMP
        ''', (key, str_value, value_type))


def delete_setting(key: str) -> bool:
    """
    Delete a setting.

    Args:
        key: Setting key

    Returns:
        True if setting was deleted, False if not found
    """
    with get_db() as conn:
        cursor = conn.execute('DELETE FROM settings WHERE key = ?', (key,))
        return cursor.rowcount > 0


def get_all_settings() -> dict[str, Any]:
    """Get all settings as a dictionary."""
    with get_db() as conn:
        cursor = conn.execute('SELECT key, value, value_type FROM settings')
        settings = {}

        for row in cursor:
            key, value, value_type = row['key'], row['value'], row['value_type']

            if value_type == 'json':
                try:
                    settings[key] = json.loads(value)
                except json.JSONDecodeError:
                    settings[key] = value
            elif value_type == 'int':
                settings[key] = int(value)
            elif value_type == 'float':
                settings[key] = float(value)
            elif value_type == 'bool':
                settings[key] = value.lower() in ('true', '1', 'yes')
            else:
                settings[key] = value

        return settings


# =============================================================================
# Signal History Functions
# =============================================================================

def add_signal_reading(
    mode: str,
    device_id: str,
    signal_strength: float,
    metadata: dict | None = None
) -> None:
    """Add a signal strength reading."""
    with get_db() as conn:
        conn.execute('''
            INSERT INTO signal_history (mode, device_id, signal_strength, metadata)
            VALUES (?, ?, ?, ?)
        ''', (mode, device_id, signal_strength, json.dumps(metadata) if metadata else None))


def get_signal_history(
    mode: str,
    device_id: str,
    limit: int = 100,
    since_minutes: int = 60
) -> list[dict]:
    """
    Get signal history for a device.

    Args:
        mode: Mode (wifi, bluetooth, adsb, etc.)
        device_id: Device identifier (MAC, ICAO, etc.)
        limit: Maximum number of readings
        since_minutes: Only get readings from last N minutes

    Returns:
        List of signal readings with timestamp
    """
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT signal_strength, timestamp, metadata
            FROM signal_history
            WHERE mode = ? AND device_id = ?
              AND timestamp > datetime('now', ?)
            ORDER BY timestamp DESC
            LIMIT ?
        ''', (mode, device_id, f'-{since_minutes} minutes', limit))

        results = []
        for row in cursor:
            results.append({
                'signal': row['signal_strength'],
                'timestamp': row['timestamp'],
                'metadata': json.loads(row['metadata']) if row['metadata'] else None
            })

        return list(reversed(results))  # Return in chronological order


def cleanup_old_signal_history(max_age_hours: int = 24) -> int:
    """
    Remove old signal history entries.

    Args:
        max_age_hours: Maximum age in hours

    Returns:
        Number of deleted entries
    """
    with get_db() as conn:
        cursor = conn.execute('''
            DELETE FROM signal_history
            WHERE timestamp < datetime('now', ?)
        ''', (f'-{max_age_hours} hours',))
        return cursor.rowcount


# =============================================================================
# Device Correlation Functions
# =============================================================================

def add_correlation(
    wifi_mac: str,
    bt_mac: str,
    confidence: float,
    metadata: dict | None = None
) -> None:
    """Add or update a device correlation."""
    with get_db() as conn:
        conn.execute('''
            INSERT INTO device_correlations (wifi_mac, bt_mac, confidence, metadata, last_seen)
            VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
            ON CONFLICT(wifi_mac, bt_mac) DO UPDATE SET
                confidence = excluded.confidence,
                last_seen = CURRENT_TIMESTAMP,
                metadata = excluded.metadata
        ''', (wifi_mac, bt_mac, confidence, json.dumps(metadata) if metadata else None))


def get_correlations(min_confidence: float = 0.5) -> list[dict]:
    """Get all device correlations above minimum confidence."""
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT wifi_mac, bt_mac, confidence, first_seen, last_seen, metadata
            FROM device_correlations
            WHERE confidence >= ?
            ORDER BY confidence DESC
        ''', (min_confidence,))

        results = []
        for row in cursor:
            results.append({
                'wifi_mac': row['wifi_mac'],
                'bt_mac': row['bt_mac'],
                'confidence': row['confidence'],
                'first_seen': row['first_seen'],
                'last_seen': row['last_seen'],
                'metadata': json.loads(row['metadata']) if row['metadata'] else None
            })

        return results


# =============================================================================
# TSCM Functions
# =============================================================================

def create_tscm_baseline(
    name: str,
    location: str | None = None,
    description: str | None = None,
    wifi_networks: list | None = None,
    bt_devices: list | None = None,
    rf_frequencies: list | None = None,
    gps_coords: dict | None = None
) -> int:
    """
    Create a new TSCM baseline.

    Returns:
        The ID of the created baseline
    """
    with get_db() as conn:
        cursor = conn.execute('''
            INSERT INTO tscm_baselines
            (name, location, description, wifi_networks, bt_devices, rf_frequencies, gps_coords)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        ''', (
            name,
            location,
            description,
            json.dumps(wifi_networks) if wifi_networks else None,
            json.dumps(bt_devices) if bt_devices else None,
            json.dumps(rf_frequencies) if rf_frequencies else None,
            json.dumps(gps_coords) if gps_coords else None
        ))
        return cursor.lastrowid


def get_tscm_baseline(baseline_id: int) -> dict | None:
    """Get a specific TSCM baseline by ID."""
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT * FROM tscm_baselines WHERE id = ?
        ''', (baseline_id,))
        row = cursor.fetchone()

        if row is None:
            return None

        return {
            'id': row['id'],
            'name': row['name'],
            'location': row['location'],
            'description': row['description'],
            'created_at': row['created_at'],
            'wifi_networks': json.loads(row['wifi_networks']) if row['wifi_networks'] else [],
            'bt_devices': json.loads(row['bt_devices']) if row['bt_devices'] else [],
            'rf_frequencies': json.loads(row['rf_frequencies']) if row['rf_frequencies'] else [],
            'gps_coords': json.loads(row['gps_coords']) if row['gps_coords'] else None,
            'is_active': bool(row['is_active'])
        }


def get_all_tscm_baselines() -> list[dict]:
    """Get all TSCM baselines."""
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT id, name, location, description, created_at, is_active
            FROM tscm_baselines
            ORDER BY created_at DESC
        ''')

        return [dict(row) for row in cursor]


def get_active_tscm_baseline() -> dict | None:
    """Get the currently active TSCM baseline."""
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT * FROM tscm_baselines WHERE is_active = 1 LIMIT 1
        ''')
        row = cursor.fetchone()

        if row is None:
            return None

        return get_tscm_baseline(row['id'])


def set_active_tscm_baseline(baseline_id: int) -> bool:
    """Set a baseline as active (deactivates others)."""
    with get_db() as conn:
        # Deactivate all
        conn.execute('UPDATE tscm_baselines SET is_active = 0')
        # Activate selected
        cursor = conn.execute(
            'UPDATE tscm_baselines SET is_active = 1 WHERE id = ?',
            (baseline_id,)
        )
        return cursor.rowcount > 0


def update_tscm_baseline(
    baseline_id: int,
    wifi_networks: list | None = None,
    bt_devices: list | None = None,
    rf_frequencies: list | None = None
) -> bool:
    """Update baseline device lists."""
    updates = []
    params = []

    if wifi_networks is not None:
        updates.append('wifi_networks = ?')
        params.append(json.dumps(wifi_networks))
    if bt_devices is not None:
        updates.append('bt_devices = ?')
        params.append(json.dumps(bt_devices))
    if rf_frequencies is not None:
        updates.append('rf_frequencies = ?')
        params.append(json.dumps(rf_frequencies))

    if not updates:
        return False

    params.append(baseline_id)

    with get_db() as conn:
        cursor = conn.execute(
            f'UPDATE tscm_baselines SET {", ".join(updates)} WHERE id = ?',
            params
        )
        return cursor.rowcount > 0


def delete_tscm_baseline(baseline_id: int) -> bool:
    """Delete a TSCM baseline."""
    with get_db() as conn:
        cursor = conn.execute(
            'DELETE FROM tscm_baselines WHERE id = ?',
            (baseline_id,)
        )
        return cursor.rowcount > 0


def create_tscm_sweep(
    sweep_type: str,
    baseline_id: int | None = None,
    wifi_enabled: bool = True,
    bt_enabled: bool = True,
    rf_enabled: bool = True
) -> int:
    """
    Create a new TSCM sweep session.

    Returns:
        The ID of the created sweep
    """
    with get_db() as conn:
        cursor = conn.execute('''
            INSERT INTO tscm_sweeps
            (baseline_id, sweep_type, wifi_enabled, bt_enabled, rf_enabled)
            VALUES (?, ?, ?, ?, ?)
        ''', (baseline_id, sweep_type, wifi_enabled, bt_enabled, rf_enabled))
        return cursor.lastrowid


def update_tscm_sweep(
    sweep_id: int,
    status: str | None = None,
    results: dict | None = None,
    anomalies: list | None = None,
    threats_found: int | None = None,
    completed: bool = False
) -> bool:
    """Update a TSCM sweep."""
    updates = []
    params = []

    if status is not None:
        updates.append('status = ?')
        params.append(status)
    if results is not None:
        updates.append('results = ?')
        params.append(json.dumps(results))
    if anomalies is not None:
        updates.append('anomalies = ?')
        params.append(json.dumps(anomalies))
    if threats_found is not None:
        updates.append('threats_found = ?')
        params.append(threats_found)
    if completed:
        updates.append('completed_at = CURRENT_TIMESTAMP')

    if not updates:
        return False

    params.append(sweep_id)

    with get_db() as conn:
        cursor = conn.execute(
            f'UPDATE tscm_sweeps SET {", ".join(updates)} WHERE id = ?',
            params
        )
        return cursor.rowcount > 0


def get_tscm_sweep(sweep_id: int) -> dict | None:
    """Get a specific TSCM sweep by ID."""
    with get_db() as conn:
        cursor = conn.execute('SELECT * FROM tscm_sweeps WHERE id = ?', (sweep_id,))
        row = cursor.fetchone()

        if row is None:
            return None

        return {
            'id': row['id'],
            'baseline_id': row['baseline_id'],
            'started_at': row['started_at'],
            'completed_at': row['completed_at'],
            'status': row['status'],
            'sweep_type': row['sweep_type'],
            'wifi_enabled': bool(row['wifi_enabled']),
            'bt_enabled': bool(row['bt_enabled']),
            'rf_enabled': bool(row['rf_enabled']),
            'results': json.loads(row['results']) if row['results'] else None,
            'anomalies': json.loads(row['anomalies']) if row['anomalies'] else [],
            'threats_found': row['threats_found']
        }


def add_tscm_threat(
    sweep_id: int,
    threat_type: str,
    severity: str,
    source: str,
    identifier: str,
    name: str | None = None,
    signal_strength: int | None = None,
    frequency: float | None = None,
    details: dict | None = None,
    gps_coords: dict | None = None
) -> int:
    """
    Add a detected threat to a TSCM sweep.

    Returns:
        The ID of the created threat
    """
    with get_db() as conn:
        cursor = conn.execute('''
            INSERT INTO tscm_threats
            (sweep_id, threat_type, severity, source, identifier, name,
             signal_strength, frequency, details, gps_coords)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            sweep_id, threat_type, severity, source, identifier, name,
            signal_strength, frequency,
            json.dumps(details) if details else None,
            json.dumps(gps_coords) if gps_coords else None
        ))
        return cursor.lastrowid


def get_tscm_threats(
    sweep_id: int | None = None,
    severity: str | None = None,
    acknowledged: bool | None = None,
    limit: int = 100
) -> list[dict]:
    """Get TSCM threats with optional filters."""
    conditions = []
    params = []

    if sweep_id is not None:
        conditions.append('sweep_id = ?')
        params.append(sweep_id)
    if severity is not None:
        conditions.append('severity = ?')
        params.append(severity)
    if acknowledged is not None:
        conditions.append('acknowledged = ?')
        params.append(1 if acknowledged else 0)

    where_clause = f'WHERE {" AND ".join(conditions)}' if conditions else ''
    params.append(limit)

    with get_db() as conn:
        cursor = conn.execute(f'''
            SELECT * FROM tscm_threats
            {where_clause}
            ORDER BY detected_at DESC
            LIMIT ?
        ''', params)

        results = []
        for row in cursor:
            results.append({
                'id': row['id'],
                'sweep_id': row['sweep_id'],
                'detected_at': row['detected_at'],
                'threat_type': row['threat_type'],
                'severity': row['severity'],
                'source': row['source'],
                'identifier': row['identifier'],
                'name': row['name'],
                'signal_strength': row['signal_strength'],
                'frequency': row['frequency'],
                'details': json.loads(row['details']) if row['details'] else None,
                'acknowledged': bool(row['acknowledged']),
                'notes': row['notes'],
                'gps_coords': json.loads(row['gps_coords']) if row['gps_coords'] else None
            })

        return results


def acknowledge_tscm_threat(threat_id: int, notes: str | None = None) -> bool:
    """Acknowledge a TSCM threat."""
    with get_db() as conn:
        if notes:
            cursor = conn.execute(
                'UPDATE tscm_threats SET acknowledged = 1, notes = ? WHERE id = ?',
                (notes, threat_id)
            )
        else:
            cursor = conn.execute(
                'UPDATE tscm_threats SET acknowledged = 1 WHERE id = ?',
                (threat_id,)
            )
        return cursor.rowcount > 0


def get_tscm_threat_summary() -> dict:
    """Get summary counts of threats by severity."""
    with get_db() as conn:
        cursor = conn.execute('''
            SELECT severity, COUNT(*) as count
            FROM tscm_threats
            WHERE acknowledged = 0
            GROUP BY severity
        ''')

        summary = {'critical': 0, 'high': 0, 'medium': 0, 'low': 0, 'total': 0}
        for row in cursor:
            summary[row['severity']] = row['count']
            summary['total'] += row['count']

        return summary
