from __future__ import annotations
import logging
from typing import Any, Optional, TYPE_CHECKING
from pathlib import Path
from allytools.types import validate_cast
from zempy.zosapi.enums.session_modes import SessionModes
from zempy.zosapi.enums.system_type import SystemType
from zempy.zosapi.system.helper import run_op
from zempy.zosapi.analysis.adapters.analyses import Analyses
from zempy.zosapi.tools.adapters.system_tools import SystemTools
from zempy.zosapi.system.adapters.metadata import Metadata
from zempy.zosapi.system.protocols.IOpticalSystem import IOpticalSystem

if TYPE_CHECKING:
    from zempy.zosapi.tools.protocols.system_tools import ISystemTools

    from zempy.zosapi.analysis.protocols.IAnalyses import IAnalyses
    from zempy.zosapi.system.protocols.IMetadata import IMetadata
    from zempy.zosapi.core.IZosapi import IZosapi

log = logging.getLogger(__name__)
class OpticalSystem:
    """ Pythonic wrapper for ZOSAPI.IOpticalSystem."""

    __slots__ = ("_native_system", "zosapi", "_tools")

    zosapi: "IZosapi"
    _tools: Optional["ISystemTools"]

    def __init__(self, zosapi: IZosapi, native_system: Any) -> None:
        self._native_system = native_system
        self.zosapi = zosapi
        self._tools: Optional[ISystemTools] = None

    def copy(self) -> IOpticalSystem:
        copied_native_system = self._native_system.CopySystem()
        return validate_cast(OpticalSystem(self.zosapi, copied_native_system), IOpticalSystem)


    def get_current_status(self) -> str:
        s = self._native_system.GetCurrentStatus()
        return "" if s is None else s

    def update_status(self) -> str:
        s = self._native_system.UpdateStatus()
        return "" if s is None else s

    @property
    def mode(self) -> SystemType:
        native = self._native_system.Mode
        return SystemType.from_native(self.zosapi, native)

    def set_mode(self, mode: SystemType) -> bool:
        if mode is SystemType.SEQUENTIAL:
            self._native_system.MakeSequential()
        elif mode is SystemType.NON_SEQUENTIAL:
            self._native_system.MakeNonSequential()
        else:
            raise ValueError(f"Unsupported SystemType: {mode!r}")
        return True

    @property
    def session_mode(self) -> SessionModes:
        native = self._native_system.SessionMode
        return SessionModes.from_native(self.zosapi, native)

    @session_mode.setter
    def session_mode(self, value: SessionModes) -> None:
        self._native_system.SessionMode = SessionModes.to_native(self.zosapi, value)

    def load_file(self, lens_file: str | Path, save_if_needed: bool = False) -> None:
        p = Path(lens_file)
        run_op(system=self,
               what=f"LoadFile({p}, save_if_needed={bool(save_if_needed)})",
               call=lambda: self._native_system.LoadFile(str(p), bool(save_if_needed)),
               check=bool)

    def new(self, save_if_needed: bool = False) -> None:
        run_op(system=self,
               what=f"New(save_if_needed={bool(save_if_needed)})",
               call=lambda: self._native_system.New(bool(save_if_needed)))

    def save(self) -> None:
        run_op(system=self, what="Save()", call=lambda: self._native_system.Save())

    def save_as(self, target: str | Path) -> None:
        p = Path(target)
        run_op(system=self, what=f"SaveAs({p})", call=lambda: self._native_system.SaveAs(str(p)))


    def close_all_analyses(self) -> None:
        try:
            analyses = self.analyses
            n = int(getattr(analyses, "NumberOfAnalyses", 0) or 0)
            for idx in range(n, 0, -1):
                try:
                    analyses.CloseAnalysis(idx)
                except Exception as e:
                    log.warning("CloseAnalysis(%d) raised: %s", idx, e)
        except Exception as e:
            log.debug("Enumerating/closing analyses failed: %s", e)

    def close(self, save_if_needed: bool = False) -> None:
        run_op(
            system=self,
            what=f"Close(save_if_needed={bool(save_if_needed)})",
            call=lambda: self._native_system.Close(bool(save_if_needed)),
            check=lambda r: r is True,
        )

    def update_file_lists(self) -> None:
        self._native_system.UpdateFileLists()

    def convert_to_project_directory(self, folder_path: str) -> bool:
        return bool(self._native_system.ConvertToProjectDirectory(folder_path))

    def turn_off_project_directory(self) -> bool:
        return bool(self._native_system.TurnOffProjectDirectory())

    def get_metadata(self) -> Optional[IMetadata]:
        native_meta = run_op(system=self,
                             what="GetMetadata()",
                             call=lambda: self._native_system.GetMetadata())
        if native_meta is None:
            log.debug("GetMetadata() returned None.")
            return None
        log.debug("IMetadata object retrieved successfully.")
        return Metadata(native_meta)


    @property
    def name(self) -> str:
        return self._native_system.SystemName

    @name.setter
    def name(self, value: str) -> None:
        self._native_system.SystemName = value

    @property
    def id(self) -> int:
        return int(self._native_system.SystemID)

    @property
    def file(self) -> str:
        return self._native_system.SystemFile

    @property
    def needs_save(self) -> bool:
        return bool(self._native_system.NeedsSave)

    @property
    def is_non_axial(self) -> bool:
        # ZOS wording is odd; exposed here as a bool directly.
        return bool(self._native_system.IsNonAxial)

    @property
    def is_project_directory(self) -> bool:
        return bool(self._native_system.IsProjectDirectory)

    @property
    def system_data(self) -> Any:      # ISystemData
        return self._native_system.SystemData

    @property
    def lde(self) -> Any:              # ILensDataEditor
        return self._native_system.LDE

    @property
    def nce(self) -> Any:              # INonSeqEditor
        return self._native_system.NCE

    @property
    def tde(self) -> Any:              # IToleranceDataEditor
        return self._native_system.TDE

    @property
    def mfe(self) -> Any:              # IMeritFunctionEditor
        return self._native_system.MFE

    @property
    def mce(self) -> Any:              # IMultiConfigEditor
        return self._native_system.MCE

    @property
    def analyses(self) -> IAnalyses:
        assert isinstance(self, IOpticalSystem)
        return Analyses(self)

    @property
    def tools(self) -> ISystemTools:
        if self._tools is None:
            self._tools = SystemTools(self.zosapi, self._native_system.Tools)
        return self._tools

    @property
    def application(self) -> Any:      # IZOSAPI_Application
        return self._native_system.zos_application

    @property
    def raw(self) -> Any:
        return self._native_system

    @property
    def update_mode(self) -> Any:
        return self._native_system.UpdateMode

    @update_mode.setter
    def update_mode(self, value: Any) -> None:
        self._native_system.UpdateMode = value

    def __repr__(self) -> str:
        return (
            f"OpticalSystem\n"
            f"  id={self.id},\n"
            f"  name={self.name!r},\n"
            f"  mode={self.mode.name},\n"
            f"  file={self.file!r},\n"
            f"  session_mode={self.session_mode}\n"
        )