"""
Project Model
Modelo de datos para manejo de proyectos y su estado
"""

import json
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Dict, List, Any, Optional


class ProjectStatus(Enum):
    """Estados posibles de un proyecto"""

    READY = "ready"
    BACKING_UP = "backing_up"
    ERROR = "error"
    FILES_IN_USE = "files_in_use"
    RETRY_PENDING = "retry_pending"
    DISABLED = "disabled"


class Project:
    """Clase para representar un proyecto individual"""

    def __init__(self, project_data: Dict[str, Any]):
        self.id = project_data.get("id", "")
        self.name = project_data.get("name", "")
        self.path = project_data.get("path", "")
        self.type = project_data.get("type", "")
        self.s7p_file = project_data.get("s7p_file", "")
        self.observation_directory = project_data.get("observation_directory", "")
        self.relative_path = project_data.get("relative_path", "")
        self.backup_path = project_data.get("backup_path", "")

        # Configuración de schedule
        schedule_config = project_data.get("schedule_config", {})
        self.schedule = schedule_config.get("schedule", "daily")
        self.schedule_time = schedule_config.get("schedule_time", "02:00")
        self.enabled = schedule_config.get("enabled", True)
        self.next_scheduled_backup = schedule_config.get("next_scheduled_backup", "")

        # Historia de backups
        backup_history = project_data.get("backup_history", {})
        self.last_backup_date = backup_history.get("last_backup_date", "")
        self.last_backup_file = backup_history.get("last_backup_file", "")
        self.backup_count = backup_history.get("backup_count", 0)
        self.last_successful_backup = backup_history.get("last_successful_backup", "")

        # Información de hash
        hash_info = project_data.get("hash_info", {})
        self.last_s7p_hash = hash_info.get("last_s7p_hash", "")
        self.last_full_hash = hash_info.get("last_full_hash", "")
        self.last_s7p_timestamp = hash_info.get("last_s7p_timestamp", "")
        self.last_s7p_size = hash_info.get("last_s7p_size", 0)
        self.last_scan_timestamp = hash_info.get("last_scan_timestamp", "")
        self.file_count = hash_info.get("file_count", 0)
        self.total_size_bytes = hash_info.get("total_size_bytes", 0)

        # Estado actual
        status = project_data.get("status", {})
        self.current_status = ProjectStatus(status.get("current_status", "ready"))
        self.last_error = status.get("last_error", None)
        self.retry_count = status.get("retry_count", 0)
        self.next_retry = status.get("next_retry", None)
        self.files_in_use = status.get("files_in_use", False)
        self.exclusivity_check_passed = status.get("exclusivity_check_passed", True)
        self.last_status_update = status.get("last_status_update", "")

        # Información de descubrimiento
        discovery_info = project_data.get("discovery_info", {})
        self.discovered_date = discovery_info.get("discovered_date", "")
        self.discovery_method = discovery_info.get("discovery_method", "")
        self.auto_discovered = discovery_info.get("auto_discovered", True)

    def to_dict(self) -> Dict[str, Any]:
        """Convertir el proyecto a diccionario para serialización JSON"""
        return {
            "id": self.id,
            "name": self.name,
            "path": self.path,
            "type": self.type,
            "s7p_file": self.s7p_file,
            "observation_directory": self.observation_directory,
            "relative_path": self.relative_path,
            "backup_path": self.backup_path,
            "schedule_config": {
                "schedule": self.schedule,
                "schedule_time": self.schedule_time,
                "enabled": self.enabled,
                "next_scheduled_backup": self.next_scheduled_backup,
            },
            "backup_history": {
                "last_backup_date": self.last_backup_date,
                "last_backup_file": self.last_backup_file,
                "backup_count": self.backup_count,
                "last_successful_backup": self.last_successful_backup,
            },
            "hash_info": {
                "last_s7p_hash": self.last_s7p_hash,
                "last_full_hash": self.last_full_hash,
                "last_s7p_timestamp": self.last_s7p_timestamp,
                "last_s7p_size": self.last_s7p_size,
                "last_scan_timestamp": self.last_scan_timestamp,
                "file_count": self.file_count,
                "total_size_bytes": self.total_size_bytes,
            },
            "status": {
                "current_status": self.current_status.value,
                "last_error": self.last_error,
                "retry_count": self.retry_count,
                "next_retry": self.next_retry,
                "files_in_use": self.files_in_use,
                "exclusivity_check_passed": self.exclusivity_check_passed,
                "last_status_update": self.last_status_update,
            },
            "discovery_info": {
                "discovered_date": self.discovered_date,
                "discovery_method": self.discovery_method,
                "auto_discovered": self.auto_discovered,
            },
        }

    def update_status(self, status: ProjectStatus, error_message: str = None) -> None:
        """Actualizar el estado del proyecto"""
        self.current_status = status
        self.last_error = error_message
        self.last_status_update = datetime.now(timezone.utc).isoformat()

        if status == ProjectStatus.ERROR:
            self.retry_count += 1
        elif status == ProjectStatus.READY:
            self.retry_count = 0
            self.last_error = None

    def update_hash_info(
        self,
        s7p_hash: str = None,
        full_hash: str = None,
        s7p_timestamp: str = None,
        s7p_size: int = None,
        file_count: int = None,
        total_size: int = None,
    ) -> None:
        """Actualizar información de hash"""
        if s7p_hash is not None:
            self.last_s7p_hash = s7p_hash
        if full_hash is not None:
            self.last_full_hash = full_hash
        if s7p_timestamp is not None:
            self.last_s7p_timestamp = s7p_timestamp
        if s7p_size is not None:
            self.last_s7p_size = s7p_size
        if file_count is not None:
            self.file_count = file_count
        if total_size is not None:
            self.total_size_bytes = total_size

        self.last_scan_timestamp = datetime.now(timezone.utc).isoformat()

    def update_backup_info(self, backup_file_path: str) -> None:
        """Actualizar información después de un backup exitoso"""
        now = datetime.now(timezone.utc).isoformat()
        self.last_backup_date = now
        self.last_successful_backup = now
        self.last_backup_file = backup_file_path
        self.backup_count += 1
        self.update_status(ProjectStatus.READY)


class ProjectManager:
    """Clase para manejar la colección de proyectos"""

    def __init__(self, projects_file_path: str = None):
        if projects_file_path is None:
            # Buscar projects.json en el directorio del proyecto
            current_dir = Path(__file__).parent.parent.parent
            projects_file_path = current_dir / "projects.json"

        self.projects_file_path = Path(projects_file_path)
        self.projects: Dict[str, Project] = {}
        self.metadata = {}
        self.statistics = {}
        self.load_projects()

    def load_projects(self) -> None:
        """Cargar proyectos desde archivo JSON"""
        try:
            if self.projects_file_path.exists():
                with open(self.projects_file_path, "r", encoding="utf-8") as f:
                    data = json.load(f)

                self.metadata = data.get("metadata", {})
                self.statistics = data.get("statistics", {})

                # Cargar proyectos
                for project_data in data.get("projects", []):
                    project = Project(project_data)
                    self.projects[project.id] = project
            else:
                # Crear archivo por defecto si no existe
                self._create_default_projects_file()
                self.save_projects()
        except Exception as e:
            raise Exception(f"Error cargando proyectos: {e}")

    def save_projects(self) -> None:
        """Guardar proyectos al archivo JSON"""
        try:
            data = {
                "metadata": self.metadata,
                "projects": [project.to_dict() for project in self.projects.values()],
                "statistics": self.statistics,
            }

            with open(self.projects_file_path, "w", encoding="utf-8") as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
        except Exception as e:
            raise Exception(f"Error guardando proyectos: {e}")

    def _create_default_projects_file(self) -> None:
        """Crear archivo de proyectos por defecto"""
        self.metadata = {
            "version": "1.0",
            "last_updated": datetime.now(timezone.utc).isoformat(),
            "total_projects": 0,
        }

        self.statistics = {
            "total_backups_created": 0,
            "total_backup_size_mb": 0.0,
            "average_backup_time_seconds": 0.0,
            "last_global_scan": "",
            "projects_with_errors": 0,
            "projects_pending_retry": 0,
        }

    def add_project(self, project_data: Dict[str, Any]) -> Project:
        """Agregar un nuevo proyecto"""
        project = Project(project_data)
        self.projects[project.id] = project
        self.metadata["total_projects"] = len(self.projects)
        self.metadata["last_updated"] = datetime.now(timezone.utc).isoformat()
        self.save_projects()
        return project

    def add_or_update_project(self, project_data: Dict[str, Any]) -> Project:
        """Agregar o actualizar un proyecto existente"""
        project_id = project_data.get("id", "")

        if project_id in self.projects:
            # Actualizar proyecto existente
            existing_project = self.projects[project_id]
            # Mantener algunos datos existentes si no se proporcionan
            if "schedule_config" not in project_data and hasattr(
                existing_project, "enabled"
            ):
                project_data["schedule_config"] = {
                    "schedule": existing_project.schedule,
                    "schedule_time": existing_project.schedule_time,
                    "enabled": existing_project.enabled,
                    "next_scheduled_backup": existing_project.next_scheduled_backup,
                }

            project = Project(project_data)
            self.projects[project.id] = project
        else:
            # Agregar nuevo proyecto
            project = Project(project_data)
            self.projects[project.id] = project

        self.metadata["total_projects"] = len(self.projects)
        self.metadata["last_updated"] = datetime.now(timezone.utc).isoformat()
        self.save_projects()
        return project

    def get_project(self, project_id: str) -> Optional[Project]:
        """Obtener un proyecto por ID"""
        return self.projects.get(project_id)

    def get_all_projects(self) -> List[Project]:
        """Obtener todos los proyectos"""
        return list(self.projects.values())

    def get_projects_by_status(self, status: ProjectStatus) -> List[Project]:
        """Obtener proyectos por estado"""
        return [p for p in self.projects.values() if p.current_status == status]

    def get_enabled_projects(self) -> List[Project]:
        """Obtener proyectos habilitados"""
        return [p for p in self.projects.values() if p.enabled]

    def update_statistics(self) -> None:
        """Actualizar estadísticas generales"""
        self.statistics["projects_with_errors"] = len(
            self.get_projects_by_status(ProjectStatus.ERROR)
        )
        self.statistics["projects_pending_retry"] = len(
            self.get_projects_by_status(ProjectStatus.RETRY_PENDING)
        )
        self.save_projects()

    def update_project_config(
        self, project_id: str, config_data: Dict[str, Any]
    ) -> bool:
        """Actualizar configuración de un proyecto específico"""
        try:
            project = self.get_project(project_id)
            if not project:
                return False

            # Actualizar configuración de schedule si se proporciona
            if "schedule_config" in config_data:
                schedule_config = config_data["schedule_config"]

                if "schedule" in schedule_config:
                    project.schedule = schedule_config["schedule"]
                if "schedule_time" in schedule_config:
                    project.schedule_time = schedule_config["schedule_time"]
                if "enabled" in schedule_config:
                    project.enabled = schedule_config["enabled"]

                # Actualizar next_scheduled_backup si es necesario
                if "next_scheduled_backup" in schedule_config:
                    project.next_scheduled_backup = schedule_config[
                        "next_scheduled_backup"
                    ]

            # Actualizar otros campos si se proporcionan
            for key, value in config_data.items():
                if hasattr(project, key) and key != "schedule_config":
                    setattr(project, key, value)

            # Actualizar metadata y guardar
            self.metadata["last_updated"] = datetime.now(timezone.utc).isoformat()
            self.save_projects()
            return True

        except Exception as e:
            print(
                f"Error actualizando configuración del proyecto " f"{project_id}: {e}"
            )
            return False
