from __future__ import annotations
from dataclasses import dataclass
from typing import Sequence, TYPE_CHECKING
from zempy.zosapi.core.interop import run_native, ensure_not_none
from zempy.bridge import zemax_exceptions as _exc
from zempy.zosapi.core.property_scalar import PropertyScalar
from zempy.zosapi.core.property_object import property_adapter
from zempy.zosapi.analysis.adapters.Message import Message
from zempy.zosapi.analysis.data.adapters.HeaderData import HeaderData
from zempy.zosapi.analysis.data.adapters.DataGrid import DataGrid
from zempy.zosapi.analysis.data.adapters.DataSeries import DataSeries
from zempy.zosapi.analysis.data.adapters.CriticalRayData import CriticalRayData
from zempy.zosapi.analysis.data.adapters.PathAnalysisData import PathAnalysisData
from zempy.zosapi.analysis.data.adapters.MetaData import MetaData
from zempy.zosapi.analysis.data.adapters.RayData import RayData
from zempy.zosapi.analysis.data.adapters.NSCSingleRayTraceData import NSCSingleRayTraceData
from zempy.zosapi.analysis.data.adapters.SpotDataResultMatrix import SpotDataResultMatrix  # if present

if TYPE_CHECKING:
    from zempy.zosapi.analysis.data.protocols.IAR_DataGrid import IAR_DataGrid
    from zempy.zosapi.analysis.data.protocols.IAR_DataGridRgb import IAR_DataGridRgb
    from zempy.zosapi.analysis.data.protocols.IAR_DataSeries import IAR_DataSeries
    from zempy.zosapi.analysis.data.protocols.IAR_DataSeriesRgb import IAR_DataSeriesRgb
    from zempy.zosapi.analysis.data.protocols.IAR_DataScatterPoints import IAR_DataScatterPoints
    from zempy.zosapi.analysis.data.protocols.IAR_DataScatterPointsRgb import IAR_DataScatterPointsRgb
    from zempy.zosapi.analysis.data.protocols.IAR_RayData import IAR_RayData
    from zempy.zosapi.analysis.data.protocols.IAR_PathAnalysisData import IAR_PathAnalysisData
    from zempy.zosapi.analysis.data.protocols.IAR_SpotDataResultMatrix import IAR_SpotDataResultMatrix
    from zempy.zosapi.analysis.data.protocols.IAR_NSCSingleRayTraceData import IAR_NSCSingleRayTraceData
    from zempy.zosapi.analysis.data.protocols.IAR_HeaderData import IAR_HeaderData
    from zempy.zosapi.analysis.data.protocols.IAR_MetaData import IAR_MetaData
    from zempy.zosapi.analysis.protocols.IMessage import IMessage


@dataclass(frozen=True, slots=True)
class IAR:
    """
    Adapter for ZOSAPI.Analysis.Data.IAR_.
    Provides access to grids/series/scatter/ray data, metadata, and text export.
    """
    zosapi: object
    native: object

    # ---- lifecycle ----
    @classmethod
    def from_native(cls, zosapi: object, native: object) -> "IAR":
        if native is None:
            raise ValueError("IAR.from_native: native is None")
        return cls(zosapi, native)

    def ensure_native(self) -> None:
        ensure_not_none(self.native, what="IAR.native", exc_type=_exc.ZemaxObjectGone)

    # ---- Methods (wrapping sub-items) ----
    def GetDataGrid(self, index: int) -> "IAR_DataGrid":
        native = run_native(
            "IAR.GetDataGrid",
            lambda: self.native.GetDataGrid(int(index)),
            ensure=self.ensure_native,
        )
        return DataGrid.from_native(self.zosapi, native)

    def GetDataGridRgb(self, index: int) -> "IAR_DataGridRgb":
        # return native (duck-typed); wrap later if you add an RGB adapter
        return run_native(
            "IAR.GetDataGridRgb",
            lambda: self.native.GetDataGridRgb(int(index)),
            ensure=self.ensure_native,
        )

    def GetDataSeries(self, index: int) -> "IAR_DataSeries":
        native = run_native(
            "IAR.GetDataSeries",
            lambda: self.native.GetDataSeries(int(index)),
            ensure=self.ensure_native,
        )
        return DataSeries.from_native(self.zosapi, native)

    def GetDataSeriesRgb(self, index: int) -> "IAR_DataSeriesRgb":
        return run_native(
            "IAR.GetDataSeriesRgb",
            lambda: self.native.GetDataSeriesRgb(int(index)),
            ensure=self.ensure_native,
        )

    def GetDataScatterPoint(self, index: int) -> "IAR_DataScatterPoints":
        return run_native(
            "IAR.GetDataScatterPoint",
            lambda: self.native.GetDataScatterPoint(int(index)),
            ensure=self.ensure_native,
        )

    def GetDataScatterPointRgb(self, index: int) -> "IAR_DataScatterPointsRgb":
        return run_native(
            "IAR.GetDataScatterPointRgb",
            lambda: self.native.GetDataScatterPointRgb(int(index)),
            ensure=self.ensure_native,
        )

    def GetRayData(self, index: int) -> "IAR_RayData":
        native = run_native(
            "IAR.GetRayData",
            lambda: self.native.GetRayData(int(index)),
            ensure=self.ensure_native,
        )
        return RayData.from_native(self.zosapi, native)

    def GetMessageAt(self, index: int) -> "IMessage":
        native = run_native(
            "IAR.GetMessageAt",
            lambda: self.native.GetMessageAt(int(index)),
            ensure=self.ensure_native,
        )
        return Message.from_native(native)

    def GetTextFile(self, Filename: str) -> bool:
        return bool(
            run_native(
                "IAR.GetTextFile",
                lambda: self.native.GetTextFile(str(Filename)),
                ensure=self.ensure_native,
            )
        )

    # ---- Scalar counters (descriptors) ----
    NumberOfDataGrids            = PropertyScalar("NumberOfDataGrids",            coerce_get=int)
    NumberOfDataGridsRgb         = PropertyScalar("NumberOfDataGridsRgb",         coerce_get=int)
    NumberOfDataSeries           = PropertyScalar("NumberOfDataSeries",           coerce_get=int)
    NumberOfDataSeriesRgb        = PropertyScalar("NumberOfDataSeriesRgb",        coerce_get=int)
    NumberOfDataScatterPoints    = PropertyScalar("NumberOfDataScatterPoints",    coerce_get=int)
    NumberOfDataScatterPointsRgb = PropertyScalar("NumberOfDataScatterPointsRgb", coerce_get=int)
    NumberOfRayData              = PropertyScalar("NumberOfRayData",              coerce_get=int)
    NumberOfMessages             = PropertyScalar("NumberOfMessages",             coerce_get=int)

    # ---- Sub-interfaces (descriptors) ----
    MetaData         = property_adapter("MetaData",         MetaData)
    HeaderData       = property_adapter("HeaderData",       HeaderData)
    CriticalRayData  = property_adapter("CriticalRayData",  CriticalRayData)
    PathAnalysisData = property_adapter("PathAnalysisData", PathAnalysisData)

    # optional: convert SpotData & NSCSingleRayTraceData to adapters
    SpotData             = property_adapter("SpotData",             SpotDataResultMatrix)
    NSCSingleRayTraceData= property_adapter("NSCSingleRayTraceData", NSCSingleRayTraceData)

    # ---- Collections ----
    @property
    def DataGrids(self) -> Sequence["IAR_DataGrid"]:
        return [self.GetDataGrid(i) for i in range(self.NumberOfDataGrids)]

    @property
    def DataGridsRgb(self) -> Sequence["IAR_DataGridRgb"]:
        return [self.GetDataGridRgb(i) for i in range(self.NumberOfDataGridsRgb)]

    @property
    def DataSeries(self) -> Sequence["IAR_DataSeries"]:
        return [self.GetDataSeries(i) for i in range(self.NumberOfDataSeries)]

    @property
    def DataSeriesRgb(self) -> Sequence["IAR_DataSeriesRgb"]:
        return [self.GetDataSeriesRgb(i) for i in range(self.NumberOfDataSeriesRgb)]

    @property
    def DataScatterPoints(self) -> Sequence["IAR_DataScatterPoints"]:
        return [self.GetDataScatterPoint(i) for i in range(self.NumberOfDataScatterPoints)]

    @property
    def DataScatterPointsRgb(self) -> Sequence["IAR_DataScatterPointsRgb"]:
        return [self.GetDataScatterPointRgb(i) for i in range(self.NumberOfDataScatterPointsRgb)]

    @property
    def RayData(self) -> Sequence["IAR_RayData"]:
        return [self.GetRayData(i) for i in range(self.NumberOfRayData)]

    @property
    def Messages(self) -> Sequence["IMessage"]:
        return [self.GetMessageAt(i) for i in range(self.NumberOfMessages)]

    # ---- repr ----
    def __repr__(self) -> str:
        try:
            return (
                f"IAR("
                f"Grids={self.NumberOfDataGrids}, GridsRgb={self.NumberOfDataGridsRgb}, "
                f"Series={self.NumberOfDataSeries}, SeriesRgb={self.NumberOfDataSeriesRgb}, "
                f"ScatterPts={self.NumberOfDataScatterPoints}, ScatterPtsRgb={self.NumberOfDataScatterPointsRgb}, "
                f"RayData={self.NumberOfRayData}, Messages={self.NumberOfMessages})"
            )
        except Exception:
            return "IAR(<unavailable>)"
