"""Status layout manager for loading and saving status area configuration.

Manages the persistent layout configuration that defines how widgets
are arranged in the status area.
"""

import copy
import logging
from dataclasses import dataclass, field
from typing import Dict, List, Any, Optional

from .widget_registry import WidgetWidth, WidthType

logger = logging.getLogger(__name__)


@dataclass
class WidgetConfig:
    """Configuration for a single widget in a row.

    Attributes:
        id: Widget identifier
        width: Width specification
    """
    id: str
    width: WidgetWidth = field(default_factory=WidgetWidth.auto)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "id": self.id,
            "width": self.width.to_dict(),
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "WidgetConfig":
        """Create from dictionary."""
        return cls(
            id=data["id"],
            width=WidgetWidth.from_dict(data.get("width", {"type": "auto"})),
        )


@dataclass
class RowConfig:
    """Configuration for a single row in the status area.

    Attributes:
        id: Row number (1-6)
        visible: Whether this row is rendered
        widgets: List of widgets in this row
    """
    id: int
    visible: bool = True
    widgets: List[WidgetConfig] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "id": self.id,
            "visible": self.visible,
            "widgets": [w.to_dict() for w in self.widgets],
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "RowConfig":
        """Create from dictionary."""
        return cls(
            id=data["id"],
            visible=data.get("visible", True),
            widgets=[WidgetConfig.from_dict(w) for w in data.get("widgets", [])],
        )


@dataclass
class StatusLayout:
    """Complete status area layout configuration.

    Attributes:
        version: Configuration version for migrations
        rows: List of row configurations
        backup: Previous configuration for restore
    """
    version: int = 1
    rows: List[RowConfig] = field(default_factory=list)
    backup: Optional[Dict[str, Any]] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        result = {
            "version": self.version,
            "rows": [r.to_dict() for r in self.rows],
        }
        if self.backup:
            result["backup"] = self.backup
        return result

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "StatusLayout":
        """Create from dictionary."""
        return cls(
            version=data.get("version", 1),
            rows=[RowConfig.from_dict(r) for r in data.get("rows", [])],
            backup=data.get("backup"),
        )

    def get_visible_rows(self) -> List[RowConfig]:
        """Get only visible rows."""
        return [r for r in self.rows if r.visible]

    def get_row(self, row_id: int) -> Optional[RowConfig]:
        """Get a row by ID."""
        for row in self.rows:
            if row.id == row_id:
                return row
        return None

    def add_row(self) -> Optional[RowConfig]:
        """Add a new row (up to 6 max).

        Returns:
            New RowConfig if added, None if at max
        """
        if len(self.rows) >= 6:
            return None

        new_id = len(self.rows) + 1
        new_row = RowConfig(id=new_id, visible=False, widgets=[])
        self.rows.append(new_row)
        return new_row


class StatusLayoutManager:
    """Manages status layout configuration.

    Handles loading, saving, and manipulating the status area layout
    configuration stored in the application config.
    """

    CONFIG_KEY = "status_layout"

    def __init__(self, config_service: Any = None):
        """Initialize the layout manager.

        Args:
            config_service: Application config service for persistence
        """
        self._config_service = config_service
        self._config_manager = getattr(config_service, 'config_manager', None) if config_service else None
        self._config_loader = getattr(config_service, 'config_loader', None) if config_service else None
        self._layout: Optional[StatusLayout] = None
        self._dirty = False
        logger.info("StatusLayoutManager initialized")

    def set_config_manager(self, config_manager: Any) -> None:
        """Set the config manager for persistence."""
        self._config_manager = config_manager

    def load(self) -> StatusLayout:
        """Load layout from config or create default.

        Returns:
            StatusLayout instance
        """
        if self._layout is not None:
            return self._layout

        # Try to load from config
        if self._config_manager:
            try:
                data = self._config_manager.get(self.CONFIG_KEY)
                if data:
                    self._layout = StatusLayout.from_dict(data)
                    logger.info(f"Loaded status layout with {len(self._layout.rows)} rows")
                    return self._layout
            except Exception as e:
                logger.error(f"Error loading status layout: {e}")

        # Create default layout
        self._layout = self._create_default_layout()
        logger.info("Created default status layout")
        return self._layout

    def _create_default_layout(self) -> StatusLayout:
        """Create the default status layout.

        Returns:
            Default StatusLayout with standard widgets
        """
        # Default row 1: main status info
        row1 = RowConfig(
            id=1,
            visible=True,
            widgets=[
                WidgetConfig(id="cwd", width=WidgetWidth.auto()),
                WidgetConfig(id="profile", width=WidgetWidth.auto()),
                WidgetConfig(id="model", width=WidgetWidth.auto()),
                WidgetConfig(id="status", width=WidgetWidth.auto()),
                WidgetConfig(id="stats", width=WidgetWidth.auto()),
            ],
        )

        # Default row 2: agent/skills (initially hidden, shows when agent active)
        row2 = RowConfig(
            id=2,
            visible=True,
            widgets=[
                WidgetConfig(id="agent", width=WidgetWidth.auto()),
                WidgetConfig(id="skills", width=WidgetWidth.auto()),
            ],
        )

        # Default row 3: tasks (initially hidden)
        row3 = RowConfig(
            id=3,
            visible=False,
            widgets=[
                WidgetConfig(id="tasks", width=WidgetWidth.auto()),
                WidgetConfig(id="tmux", width=WidgetWidth.auto()),
                WidgetConfig(id="bg-tasks", width=WidgetWidth.auto()),
            ],
        )

        # Default row 4: empty (hidden)
        row4 = RowConfig(id=4, visible=False, widgets=[])

        return StatusLayout(
            version=1,
            rows=[row1, row2, row3, row4],
        )

    def save(self) -> bool:
        """Save current layout to config.

        Returns:
            True if saved successfully
        """
        if not self._config_service:
            logger.warning("No config service - cannot save layout")
            return False

        if not self._layout:
            logger.warning("No layout to save")
            return False

        try:
            # Backup current config before saving
            current_data = self._config_service.get(self.CONFIG_KEY)
            if current_data:
                self._layout.backup = current_data

            # Update in-memory config
            self._config_service.set(self.CONFIG_KEY, self._layout.to_dict())

            # Persist to disk using config_loader (handles layered config paths)
            if self._config_loader:
                success = self._config_loader.save_merged_config(self._config_service.config_manager.config)
            else:
                # Fallback to config_manager if config_loader not available
                success = self._config_manager.save_config_file(self._config_manager.config) if self._config_manager else False

            if success:
                self._dirty = False
                logger.info("Status layout saved")
                return True
        except Exception as e:
            logger.error(f"Error saving status layout: {e}")
            return False

    def restore_backup(self) -> bool:
        """Restore layout from backup.

        Returns:
            True if restored successfully
        """
        if not self._layout or not self._layout.backup:
            logger.warning("No backup to restore")
            return False

        try:
            backup_data = self._layout.backup
            self._layout = StatusLayout.from_dict(backup_data)
            self._layout.backup = None  # Clear backup reference
            self._dirty = True
            logger.info("Status layout restored from backup")
            return True
        except Exception as e:
            logger.error(f"Error restoring backup: {e}")
            return False

    def reset_to_default(self) -> None:
        """Reset layout to default configuration."""
        # Backup current before reset
        if self._layout:
            self._layout.backup = self._layout.to_dict()

        self._layout = self._create_default_layout()
        self._dirty = True
        logger.info("Status layout reset to default")

    def get_layout(self) -> StatusLayout:
        """Get current layout (loads if needed)."""
        if self._layout is None:
            return self.load()
        return self._layout

    def is_dirty(self) -> bool:
        """Check if layout has unsaved changes."""
        return self._dirty

    def mark_dirty(self) -> None:
        """Mark layout as having unsaved changes."""
        self._dirty = True

    # =========================================================================
    # LAYOUT MANIPULATION METHODS
    # =========================================================================

    def add_widget_to_row(self, row_id: int, widget_id: str, width: WidgetWidth = None) -> bool:
        """Add a widget to a row.

        Args:
            row_id: Row to add widget to
            widget_id: Widget identifier
            width: Optional width specification

        Returns:
            True if added successfully
        """
        layout = self.get_layout()
        row = layout.get_row(row_id)

        if not row:
            logger.warning(f"Row {row_id} not found")
            return False

        if width is None:
            width = WidgetWidth.auto()

        widget = WidgetConfig(id=widget_id, width=width)
        row.widgets.append(widget)
        self._dirty = True
        logger.info(f"Added widget '{widget_id}' to row {row_id}")
        return True

    def remove_widget_from_row(self, row_id: int, widget_index: int) -> bool:
        """Remove a widget from a row by index.

        Args:
            row_id: Row to remove widget from
            widget_index: Index of widget in row

        Returns:
            True if removed successfully
        """
        layout = self.get_layout()
        row = layout.get_row(row_id)

        if not row:
            logger.warning(f"Row {row_id} not found")
            return False

        if widget_index < 0 or widget_index >= len(row.widgets):
            logger.warning(f"Widget index {widget_index} out of range")
            return False

        removed = row.widgets.pop(widget_index)
        self._dirty = True
        logger.info(f"Removed widget '{removed.id}' from row {row_id}")
        return True

    def move_widget_in_row(self, row_id: int, from_index: int, to_index: int) -> bool:
        """Move a widget within a row.

        Args:
            row_id: Row containing the widget
            from_index: Current position
            to_index: New position

        Returns:
            True if moved successfully
        """
        layout = self.get_layout()
        row = layout.get_row(row_id)

        if not row:
            return False

        if from_index < 0 or from_index >= len(row.widgets):
            return False

        if to_index < 0:
            to_index = 0
        if to_index >= len(row.widgets):
            to_index = len(row.widgets) - 1

        widget = row.widgets.pop(from_index)
        row.widgets.insert(to_index, widget)
        self._dirty = True
        logger.info(f"Moved widget '{widget.id}' from {from_index} to {to_index} in row {row_id}")
        return True

    def set_row_visibility(self, row_id: int, visible: bool) -> bool:
        """Set row visibility.

        Args:
            row_id: Row to modify
            visible: Whether row should be visible

        Returns:
            True if changed successfully
        """
        layout = self.get_layout()
        row = layout.get_row(row_id)

        if not row:
            return False

        # Don't allow hiding rows with widgets
        if not visible and row.widgets:
            logger.warning(f"Cannot hide row {row_id} - has widgets")
            return False

        row.visible = visible
        self._dirty = True
        logger.info(f"Set row {row_id} visibility to {visible}")
        return True

    def set_widget_width(self, row_id: int, widget_index: int, width: WidgetWidth) -> bool:
        """Set widget width.

        Args:
            row_id: Row containing widget
            widget_index: Widget index in row
            width: New width specification

        Returns:
            True if changed successfully
        """
        layout = self.get_layout()
        row = layout.get_row(row_id)

        if not row:
            return False

        if widget_index < 0 or widget_index >= len(row.widgets):
            return False

        row.widgets[widget_index].width = width
        self._dirty = True
        logger.info(f"Set widget width in row {row_id} at index {widget_index}")
        return True

    def add_new_row(self) -> Optional[int]:
        """Add a new row to the layout.

        Returns:
            New row ID if added, None if at max
        """
        layout = self.get_layout()
        new_row = layout.add_row()

        if new_row:
            self._dirty = True
            logger.info(f"Added new row {new_row.id}")
            return new_row.id

        logger.warning("Cannot add row - at maximum (6)")
        return None
