"""
Consent-aware telemetry service backed by Grafana Cloud OTLP.

- Base telemetry is always-on and anonymous per installation.
- Detailed analytics is sent only when the user opted in.
"""

import json
import logging
import os
import platform
import base64
import time
from datetime import datetime, timezone
from typing import Any, Dict, List, Optional
from urllib import request
from urllib.parse import urlparse

logger = logging.getLogger(__name__)

BASE_SCHEMA_KEYS = frozenset(
    {
        "install_id",
        "telemetry_fingerprint",
        "app_version",
        "platform",
        "os_version",
        "architecture",
        "locale",
        "timezone",
        "first_seen_at",
        "last_seen_at",
        "heartbeat_at",
        "release_channel",
        "deployment_type",
    }
)


def is_detailed_analytics_enabled() -> bool:
    from app.utils.telemetry import is_telemetry_enabled

    return is_telemetry_enabled()


def _build_base_telemetry_payload(event_kind: str) -> Dict[str, Any]:
    from app.config.analytics_defaults import get_analytics_config
    from app.utils.installation import get_installation_config
    from app.utils.telemetry import get_telemetry_fingerprint

    config = get_analytics_config()
    inst = get_installation_config()
    now = datetime.now(timezone.utc).isoformat()

    first_seen = inst.get_base_first_seen_sent_at() or now
    payload = {
        "install_id": inst.get_install_id(),
        "telemetry_fingerprint": get_telemetry_fingerprint(),
        "app_version": config.get("app_version", "unknown"),
        "platform": platform.system(),
        "os_version": platform.release(),
        "architecture": platform.machine(),
        "locale": (os.getenv("LANG") or os.getenv("LC_ALL") or "unknown")[:5] or "unknown",
        "timezone": os.getenv("TZ", "UTC"),
        "first_seen_at": first_seen,
        "last_seen_at": now,
        "heartbeat_at": now,
        "release_channel": os.getenv("RELEASE_CHANNEL", "default"),
        "deployment_type": "docker" if os.path.exists("/.dockerenv") else "native",
    }
    if event_kind == "first_seen":
        payload["first_seen_at"] = now
    return payload


def _otlp_enabled() -> bool:
    from app.config.analytics_defaults import get_analytics_config

    config = get_analytics_config()
    endpoint = (
        config.get("otel_exporter_otlp_endpoint")
        or os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "")
    )
    token = config.get("otel_exporter_otlp_token") or os.getenv("OTEL_EXPORTER_OTLP_TOKEN", "")
    return bool(endpoint and token)


def _build_otlp_auth_header(token: str) -> str:
    """
    Build OTLP Authorization header from a single token input.
    Accepted token formats:
    - "Basic <base64>"
    - "<instance_id>:<token>"  -> converted to Basic
    - "<base64blob>"           -> treated as Basic payload
    """
    value = (token or "").strip()
    if value.lower().startswith("basic "):
        return value
    if ":" in value:
        encoded = base64.b64encode(value.encode("utf-8")).decode("ascii")
        return f"Basic {encoded}"
    return f"Basic {value}"


def _telemetry_debug_logging_enabled() -> bool:
    return (os.getenv("OTEL_DEBUG_LOGGING", "false") or "").strip().lower() in {"1", "true", "yes", "on"}


def _remove_pii(properties: Dict[str, Any]) -> Dict[str, Any]:
    pii_keys = {"email", "username", "ip", "ip_address", "full_name", "name", "password", "token"}
    return {k: v for k, v in properties.items() if k.lower() not in pii_keys}


def event_category_for_event_name(event_name: str) -> str:
    """First segment of dotted event names; product analytics screen events use analytics."""
    if event_name.startswith("$"):
        return "analytics"
    if "." in event_name:
        return event_name.split(".", 1)[0]
    return "general"


def _otlp_correlation_attributes(event_name: str) -> List[Dict[str, Any]]:
    """trace_id, span_id, event_category for OTLP log records (no PII)."""
    rows = [
        {"key": "event_category", "value": {"stringValue": event_category_for_event_name(event_name)}},
    ]
    try:
        from app.telemetry.otel_setup import get_trace_context_for_logs, is_otel_tracing_active

        if is_otel_tracing_active():
            ctx = get_trace_context_for_logs()
            tid = ctx.get("trace_id")
            sid = ctx.get("span_id")
            if tid:
                rows.append({"key": "trace_id", "value": {"stringValue": tid}})
            if sid:
                rows.append({"key": "span_id", "value": {"stringValue": sid}})
    except Exception:
        pass
    return rows


def _to_otlp_any_value(value: Any) -> Dict[str, Any]:
    if isinstance(value, bool):
        return {"boolValue": value}
    if isinstance(value, int):
        return {"intValue": str(value)}
    if isinstance(value, float):
        return {"doubleValue": value}
    return {"stringValue": str(value)}


def _build_otlp_logs_payload(
    event_name: str,
    identity: str,
    detailed: bool,
    safe_props: Dict[str, Any],
    service_version: str,
) -> Dict[str, Any]:
    now_nanos = str(int(time.time() * 1_000_000_000))
    resource_attributes = [
        {"key": "service.name", "value": {"stringValue": "timetracker"}},
        {"key": "service.version", "value": {"stringValue": str(service_version or "unknown")}},
        {"key": "deployment.environment", "value": {"stringValue": os.getenv("FLASK_ENV", "production")}},
    ]
    record_attributes = [
        {"key": "event_name", "value": {"stringValue": event_name}},
        {"key": "identity", "value": {"stringValue": str(identity)}},
        {"key": "detailed", "value": {"boolValue": bool(detailed)}},
    ]
    record_attributes.extend(_otlp_correlation_attributes(event_name))
    for key, value in safe_props.items():
        record_attributes.append({"key": str(key), "value": _to_otlp_any_value(value)})

    return {
        "resourceLogs": [
            {
                "resource": {"attributes": resource_attributes},
                "scopeLogs": [
                    {
                        "scope": {"name": "timetracker.telemetry"},
                        "logRecords": [
                            {
                                "timeUnixNano": now_nanos,
                                "severityText": "INFO",
                                "body": {"stringValue": event_name},
                                "attributes": record_attributes,
                            }
                        ],
                    }
                ],
            }
        ]
    }


def _send_otlp_event(event_name: str, identity: str, properties: Dict[str, Any], detailed: bool) -> bool:
    from app.config.analytics_defaults import get_analytics_config

    config = get_analytics_config()
    endpoint = config.get("otel_exporter_otlp_endpoint") or os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", "")
    token = config.get("otel_exporter_otlp_token") or os.getenv("OTEL_EXPORTER_OTLP_TOKEN", "")

    if not endpoint or not token:
        if _telemetry_debug_logging_enabled():
            logger.info(
                "telemetry.skip event=%s reason=missing_otlp_config endpoint_set=%s token_set=%s",
                event_name,
                bool(endpoint),
                bool(token),
            )
        return False

    # Support OTEL-style base endpoint by auto-targeting logs path.
    endpoint = endpoint.rstrip("/")
    if endpoint.endswith("/otlp"):
        endpoint = f"{endpoint}/v1/logs"
    elif not endpoint.endswith("/v1/logs"):
        endpoint = f"{endpoint}/v1/logs"

    safe_props = _remove_pii(properties) if detailed else properties
    payload = _build_otlp_logs_payload(
        event_name=event_name,
        identity=str(identity),
        detailed=detailed,
        safe_props=safe_props,
        service_version=str(config.get("app_version", "unknown")),
    )
    body = json.dumps(payload).encode("utf-8")
    auth_header = _build_otlp_auth_header(token)
    headers = {
        "Content-Type": "application/json",
        "Authorization": auth_header,
    }
    if _telemetry_debug_logging_enabled():
        parsed = urlparse(endpoint)
        auth_mode = "basic_from_colon" if ":" in token and not token.lower().startswith("basic ") else "basic_direct"
        logger.info(
            "telemetry.send event=%s detailed=%s endpoint=%s://%s%s auth_mode=%s identity_len=%s props_count=%s",
            event_name,
            detailed,
            parsed.scheme or "https",
            parsed.netloc,
            parsed.path,
            auth_mode,
            len(str(identity)),
            len(safe_props),
        )

    req = request.Request(
        endpoint,
        data=body,
        method="POST",
        headers=headers,
    )
    try:
        with request.urlopen(req, timeout=5) as response:
            if _telemetry_debug_logging_enabled():
                logger.info("telemetry.ok event=%s status=%s", event_name, getattr(response, "status", "unknown"))
            return True
    except Exception as exc:
        logger.warning("telemetry.fail event=%s error=%s", event_name, exc)
        return False


def send_base_telemetry(payload: Dict[str, Any]) -> bool:
    install_id = payload.get("install_id")
    if not install_id:
        return False
    event_name = payload.get("_event", "base_telemetry.heartbeat")
    props = {k: v for k, v in payload.items() if k != "_event"}
    return _send_otlp_event(event_name=event_name, identity=str(install_id), properties=props, detailed=False)


def send_base_first_seen() -> bool:
    from app.utils.installation import get_installation_config

    inst = get_installation_config()
    if inst.get_base_first_seen_sent_at():
        return False
    payload = _build_base_telemetry_payload("first_seen")
    payload["_event"] = "base_telemetry.first_seen"
    payload["first_seen_at"] = datetime.now(timezone.utc).isoformat()
    if send_base_telemetry(payload):
        inst.set_base_first_seen_sent_at(payload["first_seen_at"])
        return True
    return False


def send_base_heartbeat() -> bool:
    payload = _build_base_telemetry_payload("heartbeat")
    payload["_event"] = "base_telemetry.heartbeat"
    return send_base_telemetry(payload)


def identify_user(user_id: Any, properties: Optional[Dict[str, Any]] = None) -> None:
    if not is_detailed_analytics_enabled():
        return
    _send_otlp_event("analytics.identify", str(user_id), properties or {}, detailed=True)


def send_analytics_event(user_id: Any, event_name: str, properties: Optional[Dict[str, Any]] = None) -> None:
    if not is_detailed_analytics_enabled():
        return
    from app.config.analytics_defaults import get_analytics_config
    from app.utils.installation import get_installation_config
    from app.utils.telemetry import get_telemetry_fingerprint

    config = get_analytics_config()
    enhanced = dict(properties or {})
    enhanced["install_id"] = get_installation_config().get_install_id()
    enhanced["telemetry_fingerprint"] = get_telemetry_fingerprint()
    enhanced["environment"] = os.getenv("FLASK_ENV", "production")
    enhanced["app_version"] = config.get("app_version")
    enhanced["deployment_method"] = "docker" if os.path.exists("/.dockerenv") else "native"

    try:
        from flask import request as flask_request

        if flask_request:
            enhanced["current_url"] = flask_request.url
            enhanced["host"] = flask_request.host
            enhanced["pathname"] = flask_request.path
            enhanced["browser"] = getattr(flask_request.user_agent, "browser", None)
            enhanced["device_type"] = (
                "mobile" if getattr(flask_request.user_agent, "platform", None) in ["android", "iphone"] else "desktop"
            )
            enhanced["os"] = getattr(flask_request.user_agent, "platform", None)
    except Exception:
        pass

    _send_otlp_event(event_name=event_name, identity=str(user_id), properties=enhanced, detailed=True)
