from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, Mapping, Optional, Tuple

from config.paths import DATA_ROOT
from receipts.models import AnchorReceipt, BaseReceipt, compute_hash


class AnchorBackend:
    """Abstract anchor backend."""

    name: str = "base"

    def push(self, digest: str, ts: datetime, tenant: Optional[str]) -> Tuple[str, Dict[str, object]]:
        raise NotImplementedError

    def verify(self, digest: str, anchor_id: str, proof: Mapping[str, object]) -> bool:
        raise NotImplementedError

    def describe(self) -> str:
        raise NotImplementedError


class JsonAnchorBackend(AnchorBackend):
    """Local JSON transparency log backend."""

    name = "json"

    def __init__(self, path: Path):
        self.path = path.expanduser()

    def _load(self) -> Dict[str, object]:
        if not self.path.exists():
            return {"anchors": []}
        try:
            return json.loads(self.path.read_text(encoding="utf-8"))
        except json.JSONDecodeError:
            return {"anchors": []}

    def _dump(self, payload: Mapping[str, object]) -> None:
        self.path.parent.mkdir(parents=True, exist_ok=True)
        self.path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")

    def push(self, digest: str, ts: datetime, tenant: Optional[str]) -> Tuple[str, Dict[str, object]]:
        log = self._load()
        anchors = list(log.get("anchors") or [])
        anchor_id = f"anchor-{len(anchors) + 1:06d}"
        entry = {
            "anchor_id": anchor_id,
            "digest": digest,
            "timestamp": ts.isoformat().replace("+00:00", "Z"),
            "tenant": tenant,
        }
        anchors.append(entry)
        log["anchors"] = anchors
        self._dump(log)
        proof = {"anchor_id": anchor_id, "offset": len(anchors) - 1, "digest": digest}
        return anchor_id, proof

    def verify(self, digest: str, anchor_id: str, proof: Mapping[str, object]) -> bool:
        log = self._load()
        anchors = list(log.get("anchors") or [])
        for entry in anchors:
            if entry.get("anchor_id") == anchor_id:
                return entry.get("digest") == digest
        # Fallback to proof lookup by offset.
        offset = proof.get("offset")
        if isinstance(offset, int) and 0 <= offset < len(anchors):
            return anchors[offset].get("digest") == digest and anchors[offset].get("anchor_id") == anchor_id
        return False

    def describe(self) -> str:
        return f"json:{self.path}"


def load_backend(spec: Optional[str] = None, *, log_path: Optional[Path] = None) -> AnchorBackend:
    if spec:
        if spec.startswith("json:"):
            return JsonAnchorBackend(Path(spec.split(":", 1)[1]))
        raise ValueError(f"Unsupported backend spec '{spec}'")

    if log_path:
        return JsonAnchorBackend(log_path)

    default_path = Path(os.getenv("LOOM_ANCHOR_LOG", "")) if os.getenv("LOOM_ANCHOR_LOG") else DATA_ROOT / "receipts" / "anchors.json"
    return JsonAnchorBackend(default_path)


def anchor_receipt(
    receipt: BaseReceipt,
    backend: AnchorBackend,
    *,
    tenant: Optional[str] = None,
    ts: Optional[datetime] = None,
) -> AnchorReceipt:
    timestamp = ts or datetime.now(timezone.utc)
    digest = compute_hash(receipt)
    tenant_value = tenant or receipt.tenant or receipt.domain
    anchor_id, proof = backend.push(digest=digest, ts=timestamp, tenant=tenant_value)

    anchor_receipt = AnchorReceipt(
        receipt_id=f"ANCHOR_{timestamp.strftime('%Y%m%d_%H%M%S%f')}",
        domain=receipt.domain,
        tenant=tenant_value,
        ts=timestamp,
        system_time=timestamp,
        mode=getattr(receipt, "mode", "LIVE"),
        anchor_id=anchor_id,
        anchored_receipt_id=receipt.receipt_id,
        anchored_receipt_hash=digest,
        backend=backend.describe(),
        proof=dict(proof),
        related_ids={
            "anchored_receipt_id": receipt.receipt_id,
            "anchor_id": anchor_id,
        },
        parent_ids=[receipt.receipt_id],
    )
    return anchor_receipt


def verify_anchor(
    anchor: AnchorReceipt,
    backend: AnchorBackend,
    *,
    receipt: Optional[BaseReceipt] = None,
) -> bool:
    digest = compute_hash(receipt) if receipt is not None else anchor.anchored_receipt_hash
    if receipt is not None and digest != anchor.anchored_receipt_hash:
        return False
    return backend.verify(digest=digest, anchor_id=anchor.anchor_id, proof=anchor.proof or {})
