"""map_database.py — Filesystem map database management for saltybot.

Manages the /mnt/nvme/saltybot/maps/ directory containing RTAB-Map .db files.
Each map is a single SQLite-backed RTAB-Map database file.

Directory layout:
    /mnt/nvme/saltybot/maps/
        current.db          ← active map (symlink or copy)
        home_2026-03-02.db  ← named saved maps
        garden_session2.db
        exports/
            home_2026-03-02_occ.pgm
            home_2026-03-02_occ.yaml
            home_2026-03-02_cloud.ply
"""

import os
import shutil
import time
from datetime import datetime
from pathlib import Path


class MapDatabase:
    """Manage saved RTAB-Map .db files on disk."""

    DB_SUFFIX = '.db'

    def __init__(self, maps_dir: str):
        self._maps_dir = Path(maps_dir)
        self._maps_dir.mkdir(parents=True, exist_ok=True)
        (self._maps_dir / 'exports').mkdir(exist_ok=True)

    @property
    def maps_dir(self) -> Path:
        return self._maps_dir

    @property
    def exports_dir(self) -> Path:
        return self._maps_dir / 'exports'

    def current_db_path(self) -> str:
        """Path to the active RTAB-Map database (passed to rtabmap as database_path)."""
        return str(self._maps_dir / 'current.db')

    def list_maps(self) -> list[dict]:
        """Return list of saved maps as dicts: {name, path, size_bytes, modified_time}."""
        maps = []
        for p in sorted(self._maps_dir.glob(f'*{self.DB_SUFFIX}')):
            stat = p.stat()
            maps.append({
                'name': p.stem,
                'path': str(p),
                'size_bytes': stat.st_size,
                'modified_time': datetime.fromtimestamp(stat.st_mtime).isoformat(),
            })
        return maps

    def save_as(self, map_name: str, overwrite: bool = False) -> tuple[bool, str, str]:
        """Copy current.db to <map_name>.db.

        Returns:
            (success, message, saved_path)
        """
        src = self._maps_dir / 'current.db'
        if not src.exists():
            return False, 'current.db does not exist — no active map to save', ''

        dst = self._maps_dir / f'{map_name}{self.DB_SUFFIX}'
        if dst.exists() and not overwrite:
            return False, f'{map_name}.db already exists (set overwrite=true to replace)', ''

        shutil.copy2(str(src), str(dst))
        return True, f'Saved map as {map_name}.db ({dst.stat().st_size // 1024} KB)', str(dst)

    def load_map(self, map_name: str) -> tuple[bool, str, str]:
        """Copy <map_name>.db over current.db.

        NOTE: rtabmap node must be restarted after this call to pick up the new DB.
        Returns:
            (success, message, loaded_path)
        """
        src = self._maps_dir / f'{map_name}{self.DB_SUFFIX}'
        if not src.exists():
            return False, f'Map not found: {map_name}.db', ''

        dst = self._maps_dir / 'current.db'
        # Backup current before overwriting
        if dst.exists():
            backup = self._maps_dir / f'current_backup_{int(time.time())}.db'
            shutil.copy2(str(dst), str(backup))

        shutil.copy2(str(src), str(dst))
        size_kb = dst.stat().st_size // 1024
        return (
            True,
            f'Loaded {map_name}.db → current.db ({size_kb} KB). '
            f'Restart rtabmap node to activate.',
            str(dst),
        )

    def delete_map(self, map_name: str) -> tuple[bool, str]:
        """Delete <map_name>.db.

        Cannot delete current.db through this method (use fresh_start arg instead).
        Returns:
            (success, message)
        """
        if map_name == 'current':
            return False, 'Cannot delete current.db via this service. Use fresh_start:=true at launch instead.'

        target = self._maps_dir / f'{map_name}{self.DB_SUFFIX}'
        if not target.exists():
            return False, f'Map not found: {map_name}.db'

        target.unlink()
        return True, f'Deleted {map_name}.db'

    def auto_timestamped_name(self) -> str:
        """Generate a timestamped map name for auto-saves."""
        return f'autosave_{datetime.now().strftime("%Y%m%d_%H%M%S")}'

    def prune_autosaves(self, keep_n: int = 5) -> list[str]:
        """Keep only the N most recent autosave_*.db files, delete older ones.

        Returns list of deleted file names.
        """
        autosaves = sorted(
            self._maps_dir.glob('autosave_*.db'),
            key=lambda p: p.stat().st_mtime,
        )
        to_delete = autosaves[:-keep_n] if len(autosaves) > keep_n else []
        deleted = []
        for p in to_delete:
            p.unlink()
            deleted.append(p.name)
        return deleted
