"""
Patcher for the official anthropic Python SDK.
Wraps: anthropic.messages.create (sync + async)
"""

import time
import functools
import logging
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from ..emitter import NotiLensEmitter
    from ..loop_detector import LoopDetector

logger = logging.getLogger("notilens.anthropic")

_patched = False


def patch(emitter: "NotiLensEmitter", loop_detector: "LoopDetector", timeout: float) -> bool:
    global _patched
    if _patched:
        return True
    try:
        import anthropic

        client_class = anthropic.Anthropic
        messages_class = type(client_class().messages)
        _orig = messages_class.create

        @functools.wraps(_orig)
        def _sync_create(self, *args, **kwargs):
            model = kwargs.get("model", "unknown")
            start = time.time()

            loop_msg = loop_detector.record()
            if loop_msg:
                emitter.send("task.loop", loop_msg, level="warning", meta={"model": model, "source": "anthropic"})

            emitter.send("ai.call.start", f"Anthropic call — model={model}",
                         level="info", meta={"model": model, "source": "anthropic"})
            try:
                result = _orig(self, *args, **kwargs)
                duration = round(time.time() - start, 3)

                if duration > timeout:
                    emitter.send("task.timeout", f"Anthropic call took {duration}s",
                                 level="warning", meta={"model": model, "duration": duration, "source": "anthropic"})

                usage = getattr(result, "usage", None)
                meta = {"model": model, "duration": duration, "source": "anthropic"}
                if usage:
                    meta["input_tokens"] = getattr(usage, "input_tokens", 0)
                    meta["output_tokens"] = getattr(usage, "output_tokens", 0)

                emitter.send("ai.call.complete", f"Anthropic done in {duration}s",
                             level="info", meta=meta)
                return result

            except Exception as exc:
                emitter.send("task.error", f"Anthropic error: {exc}",
                             level="error", meta={"model": model, "error": str(exc), "source": "anthropic"})
                raise

        messages_class.create = _sync_create

        # ── Async ──────────────────────────────────────────────────────────
        try:
            async_class = type(anthropic.AsyncAnthropic().messages)
            _orig_async = async_class.create

            @functools.wraps(_orig_async)
            async def _async_create(self, *args, **kwargs):
                model = kwargs.get("model", "unknown")
                start = time.time()

                loop_msg = loop_detector.record()
                if loop_msg:
                    emitter.send("task.loop", loop_msg, level="warning", meta={"model": model, "source": "anthropic"})

                emitter.send("ai.call.start", f"Anthropic async — model={model}",
                             level="info", meta={"model": model, "source": "anthropic"})
                try:
                    result = await _orig_async(self, *args, **kwargs)
                    duration = round(time.time() - start, 3)
                    emitter.send("ai.call.complete", f"Anthropic async done in {duration}s",
                                 level="info", meta={"model": model, "duration": duration, "source": "anthropic"})
                    return result
                except Exception as exc:
                    emitter.send("task.error", f"Anthropic async error: {exc}",
                                 level="error", meta={"model": model, "error": str(exc), "source": "anthropic"})
                    raise

            async_class.create = _async_create
        except Exception:
            pass

        _patched = True
        logger.debug("NotiLens: Anthropic patched")
        return True

    except ImportError:
        return False
    except Exception as e:
        logger.warning("NotiLens: failed to patch Anthropic — %s", e)
        return False
