"""File management controller for LSCSIM."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Any

from pytola_sim.lscsim.models.project_model import ProjectInfo, SimulationProject
from pytola_sim.lscsim.utils.logger import get_logger

logger = get_logger(__name__)


@dataclass
class FileOperationResult:
    """Result of file operations."""

    success: bool
    message: str
    data: Any = None
    error_code: str | None = None


class FileController:
    """Controller for file management operations."""

    def __init__(self) -> None:
        self._recent_projects: list[Path] = []
        self._current_project: SimulationProject | None = None
        self._supported_extensions = {".lscsim", ".json", ".txt"}
        logger.info("File controller initialized")

    def create_new_project(
        self,
        name: str,
        description: str = "",
        author: str = "",
        project_path: Path | None = None,
    ) -> FileOperationResult:
        """Create a new simulation project."""
        try:
            # Validate project name
            if not name or not name.strip():
                return FileOperationResult(
                    success=False,
                    message="Project name cannot be empty",
                    error_code="INVALID_NAME",
                )

            # Create project info
            info = ProjectInfo(
                name=name.strip(),
                description=description,
                author=author,
            )

            # Determine project path
            if project_path is None:
                project_path = Path.home() / "LSCSIM_Projects" / name
            project_path.mkdir(parents=True, exist_ok=True)

            # Create project
            project = SimulationProject(
                info=info,
                project_path=project_path,
                config_data={"version": "1.0.0"},
                simulation_data={},
            )

            # Save initial project file
            if project.save_to_file():
                self._current_project = project
                self._add_to_recent_projects(project_path / f"{name}.lscsim")
                logger.info(f"Created new project: {name}")
                return FileOperationResult(
                    success=True,
                    message=f"Project '{name}' created successfully",
                    data=project,
                )
            return FileOperationResult(
                success=False,
                message="Failed to save project file",
                error_code="SAVE_FAILED",
            )

        except (OSError, ValueError, TypeError) as e:
            logger.exception("Failed to create project")
            return FileOperationResult(
                success=False,
                message=f"Failed to create project: {e!s}",
                error_code="CREATION_ERROR",
            )

    def open_project(self, file_path: Path) -> FileOperationResult:
        """Open an existing project file."""
        try:
            # Validate file
            if not file_path.exists():
                return FileOperationResult(
                    success=False,
                    message=f"File not found: {file_path}",
                    error_code="FILE_NOT_FOUND",
                )

            if file_path.suffix.lower() not in self._supported_extensions:
                return FileOperationResult(
                    success=False,
                    message=f"Unsupported file type: {file_path.suffix}",
                    error_code="UNSUPPORTED_FORMAT",
                )

            # Load project
            project = SimulationProject.load_from_file(file_path)
            if project:
                self._current_project = project
                self._add_to_recent_projects(file_path)
                logger.info(f"Opened project: {project.info.name}")
                return FileOperationResult(
                    success=True,
                    message=f"Project '{project.info.name}' opened successfully",
                    data=project,
                )
            return FileOperationResult(
                success=False,
                message="Failed to load project data",
                error_code="LOAD_FAILED",
            )

        except (OSError, ValueError, TypeError):
            logger.exception("Failed to open project")
            return FileOperationResult(
                success=False,
                message="Failed to open project",
                error_code="OPEN_ERROR",
            )

    def save_project(
        self,
        project: SimulationProject | None = None,
    ) -> FileOperationResult:
        """Save current project or specified project."""
        try:
            target_project = project or self._current_project

            if not target_project:
                return FileOperationResult(
                    success=False,
                    message="No project to save",
                    error_code="NO_PROJECT",
                )

            # Update modification time
            target_project.info.modified_date = target_project.info.modified_date.__class__.now()

            if target_project.save_to_file():
                logger.info(f"Saved project: {target_project.info.name}")
                return FileOperationResult(
                    success=True,
                    message=f"Project '{target_project.info.name}' saved successfully",
                )
            return FileOperationResult(
                success=False,
                message="Failed to save project",
                error_code="SAVE_FAILED",
            )

        except (OSError, ValueError, TypeError):
            logger.exception("Failed to save project")
            return FileOperationResult(
                success=False,
                message="Failed to save project",
                error_code="SAVE_ERROR",
            )

    def save_project_as(
        self,
        file_path: Path,
        project: SimulationProject | None = None,
    ) -> FileOperationResult:
        """Save project to a new location."""
        try:
            target_project = project or self._current_project

            if not target_project:
                return FileOperationResult(
                    success=False,
                    message="No project to save",
                    error_code="NO_PROJECT",
                )

            # Update project path
            target_project.project_path = file_path.parent
            target_project.info.modified_date = target_project.info.modified_date.__class__.now()

            if target_project.save_to_file(file_path):
                self._add_to_recent_projects(file_path)
                logger.info(f"Saved project as: {file_path}")
                return FileOperationResult(
                    success=True,
                    message=f"Project saved as '{file_path.name}'",
                )
            return FileOperationResult(
                success=False,
                message="Failed to save project",
                error_code="SAVE_FAILED",
            )

        except (OSError, ValueError, TypeError):
            logger.exception("Failed to save project as")
            return FileOperationResult(
                success=False,
                message="Failed to save project",
                error_code="SAVE_ERROR",
            )

    def get_recent_projects(self) -> list[dict[str, Any]]:
        """Get list of recent projects."""
        recent_list = []
        for path in self._recent_projects:
            if not path.exists():
                continue
            try:
                # Try to load basic project info
                project = SimulationProject.load_from_file(path)
                if project:
                    recent_list.append(
                        {
                            "name": project.info.name,
                            "path": str(path),
                            "modified": project.info.modified_date.isoformat(),
                            "description": project.info.description,
                        },
                    )
            except (OSError, ValueError, TypeError):
                # If we can't load the project, still show it in recent list
                recent_list.append(
                    {
                        "name": path.stem,
                        "path": str(path),
                        "modified": "Unknown",
                        "description": "Recent project file",
                    },
                )

        return recent_list[:10]  # Return top 10 recent projects

    def _add_to_recent_projects(self, file_path: Path) -> None:
        """Add file to recent projects list."""
        # Remove if already exists
        if file_path in self._recent_projects:
            self._recent_projects.remove(file_path)

        # Add to beginning of list
        self._recent_projects.insert(0, file_path)

        # Keep only last 20 projects
        self._recent_projects = self._recent_projects[:20]

    def scan_project_directory(self, directory: Path) -> list[dict[str, Any]]:
        """Scan directory for LSCSIM project files."""
        projects = []

        if not directory.exists() or not directory.is_dir():
            return projects

        try:
            for file_path in directory.rglob("*.lscsim"):
                project_data = self._safe_load_project_for_scan(file_path)
                if project_data:
                    projects.append(project_data)

        except (OSError, ValueError, TypeError):
            logger.exception(f"Failed to scan directory {directory}")

        return projects

    def _safe_load_project_for_scan(self, file_path: Path) -> dict[str, Any] | None:
        """Safely load a project during directory scan with exception handling."""
        try:
            project = SimulationProject.load_from_file(file_path)
            if project:
                return {
                    "name": project.info.name,
                    "path": str(file_path),
                    "version": project.info.version,
                    "modified": project.info.modified_date.isoformat(),
                    "description": project.info.description,
                    "author": project.info.author,
                }
        except (OSError, ValueError, TypeError) as e:
            logger.warning(f"Failed to load project {file_path}: {e}")
        return None

    def get_current_project(self) -> SimulationProject | None:
        """Get current active project."""
        return self._current_project

    def close_project(self) -> FileOperationResult:
        """Close current project."""
        if self._current_project:
            project_name = self._current_project.info.name
            self._current_project = None
            logger.info(f"Closed project: {project_name}")
            return FileOperationResult(
                success=True,
                message=f"Project '{project_name}' closed",
            )
        return FileOperationResult(success=True, message="No project was open")

    def validate_project_name(self, name: str) -> FileOperationResult:
        """Validate project name format."""
        if not name or not name.strip():
            return FileOperationResult(
                success=False,
                message="Project name cannot be empty",
                error_code="EMPTY_NAME",
            )

        # Check for invalid characters
        invalid_chars = '<>:"/\\|?*'
        if any(char in name for char in invalid_chars):
            return FileOperationResult(
                success=False,
                message=f"Project name contains invalid characters: {invalid_chars}",
                error_code="INVALID_CHARS",
            )

        # Check length
        if len(name.strip()) > 100:
            return FileOperationResult(
                success=False,
                message="Project name is too long (maximum 100 characters)",
                error_code="NAME_TOO_LONG",
            )

        return FileOperationResult(success=True, message="Project name is valid")
