"""Project management functionality for LSCSIM."""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime, timezone
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 ProjectTemplate:
    """Project template definition."""

    name: str
    description: str
    template_data: dict[str, Any]
    created_date: datetime = field(default_factory=datetime.now)
    is_default: bool = False

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "name": self.name,
            "description": self.description,
            "template_data": self.template_data,
            "created_date": self.created_date.isoformat(),
            "is_default": self.is_default,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> ProjectTemplate:
        """Create ProjectTemplate from dictionary."""
        return cls(
            name=data["name"],
            description=data["description"],
            template_data=data["template_data"],
            created_date=datetime.fromisoformat(data["created_date"]),
            is_default=data.get("is_default", False),
        )


class ProjectManager:
    """Manager for project lifecycle operations."""

    def __init__(self) -> None:
        self._templates: list[ProjectTemplate] = []
        self._recent_projects: list[Path] = []
        self._load_default_templates()
        logger.info("Project manager initialized")

    def create_project(
        self,
        name: str,
        description: str = "",
        author: str = "",
        template_name: str | None = None,
        project_path: Path | None = None,
    ) -> SimulationProject | None:
        """Create a new project with optional template."""
        try:
            # Validate project name
            if not self._validate_project_name(name):
                logger.error(f"Invalid project name: {name}")
                return None

            # Create project info
            info = ProjectInfo(
                name=name.strip(),
                description=description,
                author=author,
                created_date=datetime.now(tz=timezone.utc),
                modified_date=datetime.now(tz=timezone.utc),
            )

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

            # Get template data
            config_data = {"version": "1.0.0"}
            simulation_data = {}

            if template_name:
                template = self.get_template(template_name)
                if template:
                    config_data.update(template.template_data.get("config", {}))
                    simulation_data.update(template.template_data.get("simulation", {}))
                    logger.info(f"Applied template: {template_name}")

            # Create project
            project = SimulationProject(
                info=info,
                project_path=project_path,
                config_data=config_data,
                simulation_data=simulation_data,
            )

            # Save initial project file
            if project.save_to_file():
                self._add_to_recent_projects(project_path / f"{name}.lscsim")
                logger.info(f"Created project: {name}")
                return project
        except (OSError, ValueError):
            logger.exception("Failed to create project")
            return None
        else:
            logger.error("Failed to save project file")
            return None

    def load_project(self, file_path: Path) -> SimulationProject | None:
        """Load an existing project from file."""
        try:
            if not file_path.exists():
                logger.error(f"Project file not found: {file_path}")
                return None

            project = SimulationProject.load_from_file(file_path)
            if project:
                self._add_to_recent_projects(file_path)
                logger.info(f"Loaded project: {project.info.name}")
                return project
        except (OSError, ValueError):
            logger.exception("Failed to load project")
            return None
        else:
            logger.error("Failed to load project data")
            return None

    def save_project(self, project: SimulationProject) -> bool:
        """Save project to file."""
        try:
            # Update modification time
            project.info.modified_date = datetime.now(tz=timezone.utc)

            if project.save_to_file():
                logger.info(f"Saved project: {project.info.name}")
                return True
        except (OSError, ValueError):
            logger.exception("Failed to save project")
            return False
        else:
            logger.error("Failed to save project")
            return False

    def get_recent_projects(self) -> list[dict[str, Any]]:
        """Get list of recent projects with metadata."""
        recent_list = []

        for path in self._recent_projects:
            if path.exists():
                try:
                    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,
                                "author": project.info.author,
                            },
                        )
                except (OSError, ValueError) as e:
                    logger.warning(f"Failed to load recent project {path}: {e}")
                    # Still include in list but with limited info
                    recent_list.append(
                        {
                            "name": path.stem,
                            "path": str(path),
                            "modified": "Unknown",
                            "description": "Recent project file",
                            "author": "Unknown",
                        },
                    )

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

    def add_template(self, template: ProjectTemplate) -> bool:
        """Add a new project template."""
        try:
            # Check if template already exists
            if any(t.name == template.name for t in self._templates):
                logger.warning(f"Template '{template.name}' already exists")
                return False

            self._templates.append(template)
        except (OSError, ValueError):
            logger.exception("Failed to add template")
            return False
        else:
            logger.info(f"Added template: {template.name}")
            return True

    def get_template(self, name: str) -> ProjectTemplate | None:
        """Get template by name."""
        for template in self._templates:
            if template.name == name:
                return template
        return None

    def get_templates(self) -> list[ProjectTemplate]:
        """Get all available templates."""
        return self._templates.copy()

    def remove_template(self, name: str) -> bool:
        """Remove template by name."""
        try:
            template = self.get_template(name)
            if template:
                self._templates.remove(template)
                logger.info(f"Removed template: {name}")
                return True
        except (OSError, ValueError):
            logger.exception("Failed to remove template")
            return False
        else:
            logger.warning(f"Template not found: {name}")
            return False

    def scan_projects_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

        for file_path in directory.rglob("*.lscsim"):
            project = SimulationProject.load_from_file(file_path)
            if project:
                projects.append(
                    {
                        "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,
                    },
                )
            else:
                logger.warning(f"Failed to load project {file_path}")
                continue

        return projects

    def _validate_project_name(self, name: str) -> bool:
        """Validate project name format."""
        if not name or not name.strip():
            return False

        # Check for invalid characters
        invalid_chars = '<>:"/\\|?*'
        if any(char in name for char in invalid_chars):
            return False

        # Check length
        return not len(name.strip()) > 100

    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 _load_default_templates(self) -> None:
        """Load default project templates."""
        default_templates = [
            ProjectTemplate(
                name="Basic Structural Analysis",
                description="Template for basic structural static analysis",
                template_data={
                    "config": {
                        "analysis_type": "static",
                        "solver": "implicit",
                        "units": "SI",
                    },
                    "simulation": {
                        "materials": ["Structural Steel"],
                        "default_mesh_size": 1.0,
                        "boundary_conditions": {},
                        "loads": {},
                    },
                },
                is_default=True,
            ),
            ProjectTemplate(
                name="Dynamic Impact Analysis",
                description="Template for explicit dynamic impact simulations",
                template_data={
                    "config": {
                        "analysis_type": "dynamic",
                        "solver": "explicit",
                        "time_step": 0.001,
                        "max_time": 1.0,
                    },
                    "simulation": {
                        "materials": ["Structural Steel", "Aluminum 6061"],
                        "default_mesh_size": 0.5,
                        "contact_definitions": [],
                    },
                },
                is_default=True,
            ),
            ProjectTemplate(
                name="Thermal Analysis",
                description="Template for thermal conduction analysis",
                template_data={
                    "config": {
                        "analysis_type": "thermal",
                        "solver": "implicit",
                        "time_step": 0.1,
                        "max_time": 100.0,
                    },
                    "simulation": {
                        "materials": ["Concrete", "Steel"],
                        "thermal_properties": {},
                        "boundary_conditions": {"temperature": [], "flux": []},
                    },
                },
                is_default=True,
            ),
        ]

        self._templates.extend(default_templates)
        logger.info(f"Loaded {len(default_templates)} default templates")
