from __future__ import annotations
from typing import Generic, ClassVar, Any, Callable, Optional
from zempy.zosapi.core.interop import run_native, ensure_not_none
from zempy.zosapi.analysis.adapters.Messages import Messages
from zempy.zosapi.analysis.protocols.IMessages import IMessages
from zempy.zosapi.core.property_object import property_adapter
from zempy.zosapi.analysis.ias.adapters.ias_field import IAS_Field
from zempy.zosapi.analysis.ias.adapters.ias_surface import IAS_Surface
from zempy.zosapi.analysis.ias.adapters.ias_wavelength import IAS_Wavelength
from zempy.zosapi.core.types_var import *

class IAS(Generic[N]):
    """
    Generic IAS adapter base: wraps Analysis Settings interfaces that expose
    Field / Surface / Wavelength sub-interfaces and a Verify/Save/Load API.
    """

    REQUIRED_NATIVE_ATTRS: ClassVar[tuple[str, ...]] = ("Field", "Surface", "Wavelength")
    NATIVE_COERCER: ClassVar[Optional[Callable[[Any], Any]]] = None

    def __init__(self, zosapi: object, native_settings: N) -> None:
        self.zosapi = zosapi
        self.native: N = native_settings

    # --- Factory ---
    @classmethod
    def from_native(cls, zosapi: object, native_base: Any) -> "IAS":
        # 1) optional subclass-specific coercion (e.g., As_* cast)
        if cls.NATIVE_COERCER is not None:
            native_base = cls.NATIVE_COERCER(native_base)

        # 2) generic validation
        missing = tuple(a for a in getattr(cls, "REQUIRED_NATIVE_ATTRS", ()) if not hasattr(native_base, a))
        if missing:
            raise TypeError(
                f"{cls.__name__} expected native with attributes {missing}, got {type(native_base).__name__}"
            )
        return cls(zosapi, native_base)

    # --- Validation ---
    def ensure_native(self) -> None:
        from zempy.bridge import zemax_exceptions as _exc
        ensure_not_none(self.native, what="IAS.native", exc_type=_exc.ZemaxObjectGone)

    # --- Methods ---
    def Verify(self) -> IMessages:
        native_messages = run_native("IAS.Verify", lambda: self.native.Verify(), ensure=self.ensure_native)
        # Prefer from_native when available; fall back to direct construction
        try:
            return Messages.from_native(self.zosapi, native_messages)  # type: ignore[attr-defined]
        except Exception:
            return Messages(self.zosapi, native_messages)

    def Save(self) -> None:
        run_native("IAS.Save", lambda: self.native.Save(), ensure=self.ensure_native)

    def Load(self) -> None:
        run_native("IAS.Load", lambda: self.native.Load(), ensure=self.ensure_native)

    def Reset(self) -> None:
        run_native("IAS.Reset", lambda: self.native.Reset(), ensure=self.ensure_native)

    def SaveTo(self, settingsFile: str) -> bool:
        return bool(run_native("IAS.SaveTo", lambda: self.native.SaveTo(str(settingsFile)), ensure=self.ensure_native))

    def LoadFrom(self, settingsFile: str) -> bool:
        return bool(run_native("IAS.LoadFrom", lambda: self.native.LoadFrom(str(settingsFile)), ensure=self.ensure_native))

    def ModifySettings(self, settingsFile: str, typeCode: str, newValue: str) -> bool:
        return bool(run_native(
            "IAS.ModifySettings",
            lambda: self.native.ModifySettings(str(settingsFile), str(typeCode), str(newValue)),
            ensure=self.ensure_native,
        ))

    # --- Sub-interfaces (descriptors) ---
    Field      = property_adapter("Field",      IAS_Field)
    Surface    = property_adapter("Surface",    IAS_Surface)
    Wavelength = property_adapter("Wavelength", IAS_Wavelength)


class IAS_Generic(IAS[Any]):
    # Generic passthrough: no required attrs beyond base (can be overridden upstream)
    REQUIRED_NATIVE_ATTRS: ClassVar[tuple[str, ...]] = ()
