from __future__ import annotations
import logging
from abc import abstractmethod, ABC
from typing import TYPE_CHECKING
from zempy.zosapi.analysis.enums.analysisIDM import AnalysisIDM
from zempy.bridge import zemax_exceptions as _exc
from zempy.zosapi.analysis.adapters.message import Message
from zempy.zosapi.analysis.iar.adapters.iar import IAR
if TYPE_CHECKING:
    from zempy.zosapi.system.protocols.IOpticalSystem import IOpticalSystem
    from zempy.zosapi.analysis.iar.protocols.iar_ import IAR_
    from zempy.zosapi.analysis.ias.protocols.ias_ import IAS_

log = logging.getLogger(__name__)
class AnalysisBase (ABC):
    """    Base class for running a ZOS analysis with settings/results wrappers (IAS/IAR).
    Requires a connected OpticalSystemAdapter; creates a native analysis window and manages it.
    """

    def __init__(self, system: IOpticalSystem, idm: AnalysisIDM) -> None:
        if system is None or getattr(system, "raw", None) is None:
            raise _exc.ZemaxSystemError("OpticalSystemAdapter is not available.")
        if getattr(system, "zosapi", None) is None:
            raise _exc.ZemaxInitializationError("ZOSAPI not available on system.")

        self.system: IOpticalSystem = system
        self.zosapi = system.zosapi
        self.idm = idm
        try:
            zos_idm = getattr(self.zosapi.Analysis.AnalysisIDM, idm.name)
        except AttributeError as e:
            raise _exc.ZemaxSystemError(f"Unknown AnalysisIDM: {idm.name}") from e

        self.analysis = self.system.analyses.New_Analysis(zos_idm)
        self.settings = self._init_settings()

    def __enter__(self) -> "AnalysisBase":
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        self.close()

    def close(self) -> None:
        """Best-effort cleanup of the native analysis window."""
        try:
            if getattr(self, "analysis", None) is not None:
                self.analysis.Close()
        except Exception:
            # Best-effort teardown: log at debug, don't raise during cleanup
            log.debug("Analysis.Close() raised during cleanup", exc_info=True)
        finally:
            self.analysis = None  # help GC

    def run(self) -> IAR_:
        """
        Apply current settings, run synchronously, and return results as IAR.
        """
        # Apply settings into the native analysis
        self.settings.apply(self.zosapi)

        # Run analysis and capture message (optional use)
        msg = self.analysis.ApplyAndWaitForCompletion()
        _ = Message.from_zos(msg)  # you can return/log this later if desired

        # Retrieve and wrap results
        native_iar = self.analysis.GetResults()
        return IAR.from_native(native_iar)

    @abstractmethod
    def _init_settings(self) -> IAS_:
        """Return a concrete IAS settings object bound/configured for this analysis."""
        ...
