"""Database service for simulation data management."""

from __future__ import annotations

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

from pytola_sim.lscsim.models.project_model import Material, SimulationProject, SimulationTemplate
from pytola_sim.lscsim.utils.config_manager import config_manager
from pytola_sim.lscsim.utils.logger import get_logger

logger = get_logger(__name__)


@dataclass
class DatabaseRecord:
    """Database record representation."""

    id: int
    name: str
    data: bytes
    memo: str
    created_at: str


class DatabaseService:
    """Database service for simulation data management."""

    def __init__(self) -> None:
        self.config = config_manager.database
        self.connection: sqlite3.Connection | None = None
        self._connect()
        logger.info("Database service initialized")

    def _connect(self) -> None:
        """Establish database connection."""
        try:
            db_path = Path(self.config.database)
            if not db_path.is_absolute():
                # Use home directory for relative paths
                db_path = Path.home() / ".lscsim" / "database" / db_path
            db_path.parent.mkdir(parents=True, exist_ok=True)

            self.connection = sqlite3.connect(str(db_path))
            self.connection.row_factory = sqlite3.Row  # Enable column access by name
            self._initialize_tables()
            logger.info(f"Database connected successfully: {db_path}")
        except Exception:
            logger.exception("Database connection failed")
            raise

    def _initialize_tables(self) -> None:
        """Create required database tables."""
        if not self.connection:
            logger.error("Database connection is not available")
            return

        cursor = self.connection.cursor()

        # Simulation templates table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS simulation_templates (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL UNIQUE,
                data BLOB,
                memo TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)

        # Materials table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS materials (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL UNIQUE,
                density REAL,
                young_modulus REAL,
                poisson_ratio REAL,
                yield_strength REAL,
                description TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)

        # Projects table
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS projects (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL UNIQUE,
                data BLOB,
                config_data BLOB,
                simulation_data BLOB,
                description TEXT,
                author TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                modified_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)

        self.connection.commit()
        logger.info("Database tables initialized")

    def save_template(self, template: SimulationTemplate) -> bool:
        """Save simulation template to database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            cursor = self.connection.cursor()
            template_json = json.dumps(template.to_dict(), ensure_ascii=False)
            cursor.execute(
                """INSERT OR REPLACE INTO simulation_templates
                   (name, data, memo) VALUES (?, ?, ?)""",
                (template.name, template_json.encode("utf-8"), template.description),
            )
            self.connection.commit()
        except Exception:
            logger.exception(f"Failed to save template {template.name}")
            return False
        else:
            logger.info(f"Template saved: {template.name}")
            return True

    def load_template(self, name: str) -> SimulationTemplate | None:
        """Load simulation template from database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return None

        try:
            cursor = self.connection.cursor()
            cursor.execute(
                "SELECT data FROM simulation_templates WHERE name = ?",
                (name,),
            )
        except Exception:
            logger.exception(f"Failed to load template {name}")
            return None
        else:
            result = cursor.fetchone()
            if result:
                template_data = json.loads(result["data"].decode("utf-8"))
                return SimulationTemplate.from_dict(template_data)
            return None

    def list_templates(self) -> list[dict[str, Any]]:
        """Get list of all templates with metadata."""
        if not self.connection:
            logger.error("Database connection is not available")
            return []

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                SELECT id, name, memo, created_at, LENGTH(data) as size_bytes
                FROM simulation_templates
                ORDER BY name
            """)

            return [
                {
                    "id": row["id"],
                    "name": row["name"],
                    "description": row["memo"],
                    "created_at": row["created_at"],
                    "size_bytes": row["size_bytes"],
                }
                for row in cursor.fetchall()
            ]
        except Exception:
            logger.exception("Failed to list templates")
            return []

    def delete_template(self, name: str) -> bool:
        """Delete template from database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute("DELETE FROM simulation_templates WHERE name = ?", (name,))
            self.connection.commit()

            if cursor.rowcount > 0:
                logger.info(f"Template deleted: {name}")
                return True
            logger.warning(f"Template not found: {name}")
        except Exception:
            logger.exception(f"Failed to delete template {name}")
            return False
        else:
            return False

    def save_material(self, material: Material) -> bool:
        """Save material to database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute(
                """INSERT OR REPLACE INTO materials
                   (name, density, young_modulus, poisson_ratio, yield_strength, description)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                (
                    material.name,
                    material.density,
                    material.young_modulus,
                    material.poisson_ratio,
                    material.yield_strength,
                    material.description,
                ),
            )
            self.connection.commit()
        except Exception:
            logger.exception(f"Failed to save material {material.name}")
            return False
        else:
            logger.info(f"Material saved: {material.name}")
            return True

    def load_material(self, name: str) -> Material | None:
        """Load material from database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return None

        try:
            cursor = self.connection.cursor()
            cursor.execute(
                """SELECT name, density, young_modulus, poisson_ratio,
                          yield_strength, description
                   FROM materials WHERE name = ?""",
                (name,),
            )
        except Exception:
            logger.exception(f"Failed to load material {name}")
            return None
        else:
            result = cursor.fetchone()
            if result:
                return Material(
                    name=result["name"],
                    density=result["density"],
                    young_modulus=result["young_modulus"],
                    poisson_ratio=result["poisson_ratio"],
                    yield_strength=result["yield_strength"],
                    description=result["description"],
                )
            return None

    def list_materials(self) -> list[dict[str, Any]]:
        """Get list of all materials with metadata."""
        if not self.connection:
            logger.error("Database connection is not available")
            return []

        try:
            cursor = self.connection.cursor()
            cursor.execute("""
                SELECT id, name, density, young_modulus, poisson_ratio,
                       yield_strength, description, created_at
                FROM materials
                ORDER BY name
            """)

            return [
                {
                    "id": row["id"],
                    "name": row["name"],
                    "density": row["density"],
                    "young_modulus": row["young_modulus"],
                    "poisson_ratio": row["poisson_ratio"],
                    "yield_strength": row["yield_strength"],
                    "description": row["description"],
                    "created_at": row["created_at"],
                }
                for row in cursor.fetchall()
            ]
        except Exception:
            logger.exception("Failed to list materials")
            return []

    def delete_material(self, name: str) -> bool:
        """Delete material from database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            cursor = self.connection.cursor()
            cursor.execute("DELETE FROM materials WHERE name = ?", (name,))
            self.connection.commit()

            if cursor.rowcount > 0:
                logger.info(f"Material deleted: {name}")
                return True
            logger.warning(f"Material not found: {name}")
        except Exception:
            logger.exception(f"Failed to delete material {name}")
            return False
        else:
            return False

    def search_templates(self, query: str) -> list[dict[str, Any]]:
        """Search templates by name or description."""
        if not self.connection:
            logger.error("Database connection is not available")
            return []

        try:
            cursor = self.connection.cursor()
            search_term = f"%{query}%"
            cursor.execute(
                """
                SELECT id, name, memo, created_at, LENGTH(data) as size_bytes
                FROM simulation_templates
                WHERE name LIKE ? OR memo LIKE ?
                ORDER BY name
            """,
                (search_term, search_term),
            )

            return [
                {
                    "id": row["id"],
                    "name": row["name"],
                    "description": row["memo"],
                    "created_at": row["created_at"],
                    "size_bytes": row["size_bytes"],
                }
                for row in cursor.fetchall()
            ]
        except Exception:
            logger.exception("Failed to search templates")
            return []

    def get_database_stats(self) -> dict[str, Any]:
        """Get database statistics."""
        if not self.connection:
            logger.error("Database connection is not available")
            return {}

        try:
            cursor = self.connection.cursor()

            # Get counts
            cursor.execute("SELECT COUNT(*) as count FROM simulation_templates")
            template_count = cursor.fetchone()["count"]

            cursor.execute("SELECT COUNT(*) as count FROM materials")
            material_count = cursor.fetchone()["count"]

            cursor.execute("SELECT COUNT(*) as count FROM projects")
            project_count = cursor.fetchone()["count"]

            # Get total size
            cursor.execute("""
                SELECT SUM(LENGTH(data) + LENGTH(config_data) + LENGTH(simulation_data)) as total_size
                FROM projects
            """)
            total_size = cursor.fetchone()["total_size"] or 0

            return {
                "templates_count": template_count,
                "materials_count": material_count,
                "projects_count": project_count,
                "total_data_size_bytes": total_size,
                "database_path": str(Path(self.config.database)),
            }
        except Exception:
            logger.exception("Failed to get database stats")
            return {}

    def backup_database(self, backup_path: Path) -> bool:
        """Create database backup."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            backup_path.parent.mkdir(parents=True, exist_ok=True)

            # Use SQLite backup API
            backup_conn = sqlite3.connect(str(backup_path))
            self.connection.backup(backup_conn)
            backup_conn.close()
        except Exception:
            logger.exception("Failed to backup database")
            return False
        else:
            logger.info(f"Database backed up to: {backup_path}")
            return True

    def save_project(self, project: SimulationProject) -> bool:
        """Save project to database."""
        if not self.connection:
            logger.error("Database connection is not available")
            return False

        try:
            cursor = self.connection.cursor()
            project_json = json.dumps(project.info.to_dict(), ensure_ascii=False)
            config_json = json.dumps(project.config_data, ensure_ascii=False)
            sim_json = json.dumps(project.simulation_data, ensure_ascii=False)

            cursor.execute(
                """INSERT OR REPLACE INTO projects
                   (name, data, config_data, simulation_data, description, author)
                   VALUES (?, ?, ?, ?, ?, ?)""",
                (
                    project.info.name,
                    project_json.encode("utf-8"),
                    config_json.encode("utf-8"),
                    sim_json.encode("utf-8"),
                    project.info.description,
                    project.info.author,
                ),
            )
            self.connection.commit()
        except Exception:
            logger.exception(f"Failed to save project {project.info.name}")
            return False
        else:
            logger.info(f"Project saved: {project.info.name}")
            return True

    def close(self) -> None:
        """Close database connection."""
        if self.connection:
            self.connection.close()
            logger.info("Database connection closed")
