from __future__ import annotations

import contextlib
import functools
import logging
import random
import traceback
from pathlib import Path

from bluequbit.bluequbit_logger import BlueQubitLoggerFormatter, PrefixLoggerAdapter

logger_sdk = logging.getLogger("bluequbit-python-sdk")


def error_log_wrapper(func):
    """Wrap a method to emit a best-effort traceback via ``self.logger``."""

    @functools.wraps(func)
    def error_log_wrapped(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            *_, (last_frame, _) = traceback.walk_tb(e.__traceback__)
            lines = ["Traceback (most recent call last):\n"]
            lines.extend(traceback.format_stack(last_frame))
            lines.append(type(e).__name__ + ": " + str(e))
            error_info = "".join(lines)
            with contextlib.suppress(Exception):
                args[0].logger.error(error_info)
            raise

    return error_log_wrapped


class ErrorLoggingBase:
    """Apply ``error_log_wrapper`` to subclass callables automatically."""

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        for name, method in cls.__dict__.items():
            if callable(method):
                setattr(cls, name, error_log_wrapper(method))


class ArtifactLogger:
    """Base class for SDK components with prefixed logging and artifact log files."""

    def __init__(self, artifacts_output_dir: str | Path | None, logger_name: str):
        super().__init__()
        self.artifacts_output_dir: Path | None
        if artifacts_output_dir is not None:
            self.artifacts_output_dir = Path(artifacts_output_dir)
            self.artifacts_output_dir.mkdir(parents=True, exist_ok=True)
            logger = logging.getLogger(
                f"bq_sdk_artifact_{logger_name}_{random.randint(0, 1_000_000_000)}"
            )
            logger.parent = logger_sdk
            logger.propagate = True
            file_handler = logging.FileHandler(
                self.artifacts_output_dir / f"{logger_name}.log", mode="w"
            )
            file_handler.setLevel(logger_sdk.level)
            file_handler.setFormatter(BlueQubitLoggerFormatter(colored=False))
            logger.addHandler(file_handler)
        else:
            logger = logger_sdk
            self.artifacts_output_dir = None
        self.logger = PrefixLoggerAdapter(logger, {"prefix": logger_name})


# Backward compatibility for existing hierarchical-circuit imports.
HierCircuitLogger = ArtifactLogger
