"""Anthropic kullanım istatistiklerini structured JSON-lines log'a yazan modül.

Mimari:
    Her `messages.create` / `messages.stream` çağrısından sonra `record_usage`
    çağrılır. JSONL formatında /var/log/patentyazar-llm-usage.log (production)
    veya /tmp/llm-usage.log (development) dosyasına atomic append yapar.

Laravel tarafında `php artisan llm:ingest-usage` cron komutu bu dosyayı okur,
`llm_usage_records` tablosuna ekler ve dosyayı truncate eder.

Bu yüzden file-based loglama tercih ettik (Redis pub-sub veya başka bir kanal
yerine):
  - Python ve Laravel ayrı süreçlerde, file ortak alan
  - Apache + systemd standart, bir yere bağımlılık eklemek istemiyoruz
  - Append atomic (POSIX < PIPE_BUF, log satırları küçük)
  - Cron-based ingest 5 dakikada bir → admin paneli için zaten yeterli
"""

from __future__ import annotations

import json
import logging
import os
import time
from pathlib import Path
from typing import Any

from app.config import get_settings

_logger = logging.getLogger(__name__)

# Production'da systemd journal'ı uvicorn user'ına yazma izni verdiği için
# bu path'in /var/log/ altında olması gerekir. Dev'de /tmp.
_DEFAULT_LOG_PATH = "/var/log/patentyazar-llm-usage.log"
_DEV_LOG_PATH = "/tmp/llm-usage.log"


def _log_path() -> str:
    """Configured log path; production'da /var/log/ user yazabiliyor mu kontrol."""
    settings = get_settings()
    candidate = getattr(settings, "llm_usage_log_path", None)
    if candidate:
        return candidate

    # Production: /var/log/patentyazar-llm-usage.log'a yazabiliyor muyuz?
    #
    # Deploy, dosyayı önceden servis user'ı sahipliğinde oluşturur (bkz.
    # deploy/) — bu sayede /var/log dizininin kendisi yazılabilir olmasa
    # bile servis dosyaya append edebilir. Bu yüzden önce DOSYANIN kendisini
    # kontrol et; yoksa parent dizine düş. Dizin testi tek başına yanlış:
    # /var/log çoğu dağıtımda root:syslog ve servis user'ına kapalıdır,
    # o durumda kayıtlar systemd PrivateTmp altındaki /tmp'ye düşer ve
    # Laravel ingest cron'u bunları asla göremez.
    log_file = Path(_DEFAULT_LOG_PATH)
    if log_file.exists():
        if os.access(log_file, os.W_OK):
            return _DEFAULT_LOG_PATH
    elif os.access(log_file.parent, os.W_OK):
        return _DEFAULT_LOG_PATH
    return _DEV_LOG_PATH


def record_usage(
    *,
    model: str,
    usage: Any,
    endpoint: str,
    extra: dict[str, Any] | None = None,
) -> None:
    """Anthropic response'undaki `usage` objesini log'a yazar.

    `usage` Anthropic SDK'nın `Usage` modeli olabilir veya stream end_event'inden
    gelmiş bir dict olabilir. İki tipten de aşağıdaki alanları çeker:

      input_tokens                     — non-cached input
      output_tokens                    — completion
      cache_creation_input_tokens      — yeni cache write (5min veya 1h)
      cache_read_input_tokens          — cache hit (en ucuz)

    Bu alanların hepsi int. Eksik olan 0 sayılır.

    `endpoint` log'lama için string identifier — örn. "drafting.stream",
    "intake.suggest", "prior_art.analyze". Admin paneli endpoint bazında
    grupluyor.

    Hatalar yutulur — logging code uygulama akışını hiçbir zaman bozmamalı.
    """
    try:
        if usage is None:
            return

        def _get(key: str) -> int:
            if hasattr(usage, key):
                return int(getattr(usage, key) or 0)
            if isinstance(usage, dict):
                return int(usage.get(key, 0) or 0)
            return 0

        record: dict[str, Any] = {
            "ts": int(time.time() * 1000),  # epoch ms
            "model": model,
            "endpoint": endpoint,
            "input_tokens": _get("input_tokens"),
            "output_tokens": _get("output_tokens"),
            "cache_creation_input_tokens": _get("cache_creation_input_tokens"),
            "cache_read_input_tokens": _get("cache_read_input_tokens"),
        }
        if extra:
            record["extra"] = extra

        line = json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n"

        # Atomic append — POSIX guarantees writes < PIPE_BUF (4096 on Linux)
        # are atomic. Bizim line ~150 byte; sorun yok.
        with open(_log_path(), "a", encoding="utf-8") as f:
            f.write(line)

    except Exception:  # noqa: BLE001
        # Asla uygulama akışını bozma — log bir şey loglamayı becerememesine
        # rağmen çalışmaya devam etmeli. Bu yüzden Exception'ı bastırıyoruz.
        _logger.exception("LLM usage log failed (suppressed)")
