"""Organization-Settings-Helper.

KV-Store pro Organisation. Aktuell genutzt fuer:
  - output_language ('de'|'en'|...) - Anzeige-/Lagebild-Sprache
  - source_language_whitelist (JSON-Liste, z.B. ["ja"]) - schraenkt RSS/Telegram-Quellen ein
  - research_language (ISO-Code) - steuert WebSearch-Prompts (default = output_language)
  - translator_enabled ('true'|'false') - override fuer das globale TRANSLATOR_ENABLED-Flag

Cache: TTL 60s in-memory pro (tenant_id, key). Wird bei set_org_setting()
invalidiert.
"""
import json
import logging
import os
import time
from typing import Optional

import aiosqlite

logger = logging.getLogger("osint.org_settings")

_CACHE: dict[tuple[int, str], tuple[float, Optional[str]]] = {}
_TTL_SECONDS = 60.0


def _cache_get(tenant_id: int, key: str) -> tuple[bool, Optional[str]]:
    """(hit, value). hit=True heisst Cache traf; value kann auch None sein."""
    entry = _CACHE.get((tenant_id, key))
    if entry is None:
        return (False, None)
    expires_at, value = entry
    if time.monotonic() > expires_at:
        _CACHE.pop((tenant_id, key), None)
        return (False, None)
    return (True, value)


def _cache_put(tenant_id: int, key: str, value: Optional[str]) -> None:
    _CACHE[(tenant_id, key)] = (time.monotonic() + _TTL_SECONDS, value)


def _cache_invalidate(tenant_id: int, key: str) -> None:
    _CACHE.pop((tenant_id, key), None)


async def get_org_setting(
    db: aiosqlite.Connection,
    tenant_id: int,
    key: str,
    default: Optional[str] = None,
) -> Optional[str]:
    """Liest ein Org-Setting. Fallback auf default."""
    if tenant_id is None:
        return default
    hit, cached = _cache_get(tenant_id, key)
    if hit:
        return cached if cached is not None else default
    cursor = await db.execute(
        "SELECT value FROM organization_settings WHERE organization_id = ? AND key = ?",
        (tenant_id, key),
    )
    row = await cursor.fetchone()
    value = row["value"] if row else None
    _cache_put(tenant_id, key, value)
    return value if value is not None else default


async def set_org_setting(
    db: aiosqlite.Connection,
    tenant_id: int,
    key: str,
    value: str,
) -> None:
    """Setzt ein Org-Setting (upsert)."""
    await db.execute(
        """INSERT INTO organization_settings (organization_id, key, value, updated_at)
           VALUES (?, ?, ?, CURRENT_TIMESTAMP)
           ON CONFLICT(organization_id, key) DO UPDATE SET
             value = excluded.value,
             updated_at = CURRENT_TIMESTAMP""",
        (tenant_id, key, value),
    )
    await db.commit()
    _cache_invalidate(tenant_id, key)
    logger.info("Org %s Setting %s='%s' gespeichert", tenant_id, key, value)


# Bekannte Sprachen + Anzeigenamen fuer Prompts
LANGUAGE_DISPLAY_NAMES = {
    "de": "Deutsch",
    "en": "English",
    "ja": "Japanese",
    "zh": "Chinese",
    "ko": "Korean",
    "ru": "Russian",
    "ar": "Arabic",
    "fa": "Persian",
    "he": "Hebrew",
    "fr": "French",
    "es": "Spanish",
}


async def get_org_language(
    db: aiosqlite.Connection,
    tenant_id: int,
) -> str:
    """Liefert ISO-2-Sprachcode der Org (default 'de').

    Steuert die Lagebild-/Anzeige-Sprache.
    """
    value = await get_org_setting(db, tenant_id, "output_language", default="de")
    if value not in LANGUAGE_DISPLAY_NAMES:
        logger.warning("Unbekannte output_language '%s' fuer Org %s -- fallback 'de'", value, tenant_id)
        return "de"
    return value


async def get_source_language_whitelist(
    db: aiosqlite.Connection,
    tenant_id: int,
) -> Optional[list[str]]:
    """Liefert Liste erlaubter Quellsprachen oder None (= keine Einschränkung).

    Gespeichert als JSON-Array unter dem Key 'source_language_whitelist'.
    Beispiel-Wert: '["ja"]' -> nur japanischsprachige Quellen.
    """
    raw = await get_org_setting(db, tenant_id, "source_language_whitelist", default=None)
    if not raw:
        return None
    try:
        parsed = json.loads(raw)
    except (json.JSONDecodeError, TypeError) as e:
        logger.warning(
            "source_language_whitelist fuer Org %s ist kein JSON ('%s'): %s",
            tenant_id, raw, e,
        )
        return None
    if not isinstance(parsed, list):
        logger.warning("source_language_whitelist fuer Org %s ist keine Liste: %r", tenant_id, parsed)
        return None
    cleaned = [str(x).strip().lower() for x in parsed if str(x).strip()]
    return cleaned or None


async def get_research_language(
    db: aiosqlite.Connection,
    tenant_id: int,
) -> str:
    """Liefert die Sprache, in der der WebSearch-Researcher primär sucht.

    Default = output_language. Bei jp_demo z.B. 'ja', während output_language='de' bleibt.
    """
    value = await get_org_setting(db, tenant_id, "research_language", default=None)
    if value and value in LANGUAGE_DISPLAY_NAMES:
        return value
    return await get_org_language(db, tenant_id)


async def get_translator_enabled(
    db: aiosqlite.Connection,
    tenant_id: Optional[int],
) -> bool:
    """Liefert true wenn der (volle) Translator-Schritt fuer diese Org laufen soll.

    Hierarchie:
      1. Org-Setting 'translator_enabled' ('true'/'false') gewinnt, wenn gesetzt.
      2. Sonst: globales ENV-Flag TRANSLATOR_ENABLED (Default true im config.py).
    """
    if tenant_id is not None:
        raw = await get_org_setting(db, tenant_id, "translator_enabled", default=None)
        if raw is not None:
            return str(raw).strip().lower() in ("true", "1", "yes", "on")
    env_value = os.environ.get("TRANSLATOR_ENABLED", "true").strip().lower()
    return env_value in ("true", "1", "yes", "on")


def language_display(lang_iso: str) -> str:
    """ISO-Code -> Anzeigename fuer Prompts ('de' -> 'Deutsch')."""
    return LANGUAGE_DISPLAY_NAMES.get(lang_iso, lang_iso)
