"""LMDB translation storage."""

import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path

import lmdb

# 1 GB default map size — plenty for translation caches
_DEFAULT_MAP_SIZE = 1024 * 1024 * 1024


def _content_hash(source_text: str, project_context_hash: str, string_context_hash: str) -> str:
    return hashlib.sha256(
        (source_text + "\x00" + project_context_hash + "\x00" + string_context_hash).encode()
    ).hexdigest()


def _make_key(source_lang: str, target_lang: str, chash: str, plural_category: str) -> bytes:
    return f"{source_lang}|{target_lang}|{chash}|{plural_category}".encode()


def _parse_key(key: bytes) -> dict:
    parts = key.decode().split("|")
    return {
        "source_lang": parts[0],
        "target_lang": parts[1],
        "content_hash": parts[2],
        "plural_category": parts[3] if len(parts) > 3 else "",
    }


class TranslationStore:
    def __init__(self, db_path: Path):
        self._db_path = Path(db_path)
        self._env: lmdb.Environment | None = None

    def initialize(self):
        # Ensure parent directory exists
        self._db_path.parent.mkdir(parents=True, exist_ok=True)
        self._env = lmdb.open(
            str(self._db_path),
            map_size=_DEFAULT_MAP_SIZE,
            max_dbs=0,
            subdir=True,
        )

    def _get_env(self) -> lmdb.Environment:
        if self._env is None:
            raise RuntimeError("Store not initialized")
        return self._env

    def lookup(
        self,
        source_text: str,
        source_lang: str,
        target_lang: str,
        project_context_hash: str,
        string_context_hash: str,
    ) -> str | None:
        env = self._get_env()
        chash = _content_hash(source_text, project_context_hash, string_context_hash)
        key = _make_key(source_lang, target_lang, chash, "")
        with env.begin() as txn:
            raw = txn.get(key)
            if raw is None:
                return None
            entry = json.loads(raw)
            if entry["status"] == "translated":
                return entry["translated_text"]
            return None

    def insert(
        self,
        source_text: str,
        source_lang: str,
        target_lang: str,
        project_context_hash: str,
        string_context_hash: str,
        translated_text: str,
        model: str,
        status: str = "translated",
    ):
        env = self._get_env()
        chash = _content_hash(source_text, project_context_hash, string_context_hash)
        key = _make_key(source_lang, target_lang, chash, "")
        value = json.dumps({
            "source_text": source_text,
            "translated_text": translated_text,
            "model": model,
            "status": status,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "project_context_hash": project_context_hash,
            "string_context_hash": string_context_hash,
        }).encode()
        with env.begin(write=True) as txn:
            # ON CONFLICT DO NOTHING — overwrite=False skips if key exists
            txn.put(key, value, overwrite=False)

    def lookup_plural(
        self,
        source_text: str,
        source_lang: str,
        target_lang: str,
        project_context_hash: str,
        string_context_hash: str,
    ) -> dict[str, str]:
        env = self._get_env()
        chash = _content_hash(source_text, project_context_hash, string_context_hash)
        prefix = _make_key(source_lang, target_lang, chash, "")
        result: dict[str, str] = {}
        with env.begin() as txn:
            cursor = txn.cursor()
            if cursor.set_range(prefix):
                for key, raw in cursor:
                    if not key.startswith(prefix):
                        break
                    parsed = _parse_key(key)
                    # Skip regular entries (empty plural_category)
                    if not parsed["plural_category"]:
                        continue
                    entry = json.loads(raw)
                    if entry["status"] == "translated":
                        result[parsed["plural_category"]] = entry["translated_text"]
        return result

    def insert_plural(
        self,
        source_text: str,
        source_lang: str,
        target_lang: str,
        project_context_hash: str,
        string_context_hash: str,
        plural_category: str,
        translated_text: str,
        model: str,
        status: str = "translated",
    ):
        env = self._get_env()
        chash = _content_hash(source_text, project_context_hash, string_context_hash)
        key = _make_key(source_lang, target_lang, chash, plural_category)
        value = json.dumps({
            "source_text": source_text,
            "translated_text": translated_text,
            "model": model,
            "status": status,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "project_context_hash": project_context_hash,
            "string_context_hash": string_context_hash,
        }).encode()
        with env.begin(write=True) as txn:
            txn.put(key, value, overwrite=False)

    def stats(self) -> dict:
        env = self._get_env()
        total = 0
        failed = 0
        by_language: dict[str, int] = {}
        with env.begin() as txn:
            cursor = txn.cursor()
            for key, raw in cursor:
                parsed = _parse_key(key)
                entry = json.loads(raw)
                if entry["status"] == "translated":
                    total += 1
                    lang = parsed["target_lang"]
                    by_language[lang] = by_language.get(lang, 0) + 1
                elif entry["status"] == "failed":
                    failed += 1
        return {
            "total_translations": total,
            "total_failed": failed,
            "by_language": by_language,
        }

    def count(self, target_lang: str | None = None, failed_only: bool = False) -> int:
        """Count entries matching optional filters."""
        env = self._get_env()
        n = 0
        with env.begin() as txn:
            cursor = txn.cursor()
            for key, raw in cursor:
                parsed = _parse_key(key)
                if target_lang and parsed["target_lang"] != target_lang:
                    continue
                if failed_only:
                    entry = json.loads(raw)
                    if entry["status"] != "failed":
                        continue
                n += 1
        return n

    def delete(self, target_lang: str | None = None, failed_only: bool = False) -> int:
        """Delete entries matching optional filters. Returns count deleted."""
        env = self._get_env()
        to_delete: list[bytes] = []
        with env.begin() as txn:
            cursor = txn.cursor()
            for key, raw in cursor:
                parsed = _parse_key(key)
                if target_lang and parsed["target_lang"] != target_lang:
                    continue
                if failed_only:
                    entry = json.loads(raw)
                    if entry["status"] != "failed":
                        continue
                to_delete.append(key)
        with env.begin(write=True) as txn:
            for k in to_delete:
                txn.delete(k)
        return len(to_delete)

    def close(self):
        if self._env is not None:
            self._env.close()
            self._env = None
