"""SQLite-backed TLS reference data access — replaces ``TlsDB.csv``.

Provides functions to query the ``tls_ref_data`` table (populated by
the ``import_tlsdb`` management command) instead of reading the
now-deprecated ``TlsDB.csv`` file.

Usage::

    from analysis.tls_ref import get_tls_ref_data, get_tls_field, get_tls_ref_dict

    # All rows as list of dicts
    all_fields = get_tls_ref_data()

    # Single field
    field = get_tls_field('0ver')
    # -> {'field_code': '0ver', 'meaning': 'TLS版本', 'data_type': '2字节'}

    # Dict lookup (cached in memory after first call)
    lookup = get_tls_ref_dict()
    # -> {'0ver': {'meaning': 'TLS版本', 'data_type': '2字节'}, ...}
"""

from functools import lru_cache
from typing import Optional

from django.db import connection


def get_tls_ref_data() -> list[dict[str, str]]:
    """Return all rows from ``tls_ref_data`` as a list of dicts.

    Returns:
        Each dict has keys ``field_code``, ``meaning``, ``data_type``.
        Returns an empty list if the table does not exist (e.g. before
        migration).
    """
    if not _table_exists():
        return []

    with connection.cursor() as cursor:
        cursor.execute(
            'SELECT field_code, meaning, data_type FROM tls_ref_data ORDER BY id'
        )
        columns = [col[0] for col in cursor.description]
        return [dict(zip(columns, row)) for row in cursor.fetchall()]


def get_tls_field(field_code: str) -> Optional[dict[str, str]]:
    """Return a single TLS field definition by its code.

    Args:
        field_code: e.g. ``'0ver'``, ``':ips'``, ``'cipher-suite'``.

    Returns:
        Dict with keys ``field_code``, ``meaning``, ``data_type``, or
        ``None`` if not found.
    """
    if not _table_exists():
        return None

    with connection.cursor() as cursor:
        cursor.execute(
            'SELECT field_code, meaning, data_type FROM tls_ref_data WHERE field_code = %s',
            [field_code],
        )
        row = cursor.fetchone()
        if row is None:
            return None
        return {
            'field_code': row[0],
            'meaning': row[1],
            'data_type': row[2],
        }


@lru_cache(maxsize=1)
def get_tls_ref_dict() -> dict[str, dict[str, str]]:
    """Return a dict mapping ``field_code -> {meaning, data_type}``.

    The result is cached in memory after the first call. Call
    ``get_tls_ref_dict.cache_clear()`` to invalidate if the
    underlying table is modified at runtime.
    """
    rows = get_tls_ref_data()
    return {r['field_code']: {'meaning': r['meaning'], 'data_type': r['data_type']} for r in rows}


def _table_exists() -> bool:
    """Check if the ``tls_ref_data`` table exists in the database."""
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT name FROM sqlite_master WHERE type='table' AND name='tls_ref_data'"
        )
        return cursor.fetchone() is not None
