"""
Canvas Metadata Service

This service provides unified management for all canvas types:
- General canvases: Free-form canvases for creative work
- Asset canvases: Canvases bound to specific assets (characters, scenes, props)
- Storyboard canvases: Canvases bound to specific storyboards

The service handles CRUD operations, querying, and lifecycle management
for canvas metadata stored in the canvas_metadata table.
"""

from typing import Optional, List
from sqlmodel import Session, select
from src.models.entities import CanvasMetadataDB, CanvasDB, AssetDB, StoryboardDB
from src.models.schemas import CanvasMetadata, CreateGeneralCanvasRequest
from src.mappers import CanvasMetadataMapper
import uuid
from datetime import datetime
import logging

logger = logging.getLogger(__name__)


class CanvasMetadataService:
    """Service for managing canvas metadata across all canvas types."""
    
    def __init__(self, session: Session):
        """Initialize the service with a database session.
        
        Args:
            session: SQLModel database session
        """
        self.session = session
    
    def list_canvases(
        self, 
        project_id: str,
        canvas_type: Optional[str] = None,
        include_deleted: bool = False
    ) -> List[CanvasMetadataDB]:
        """Query canvas list for a project.
        
        Args:
            project_id: Project ID to filter by
            canvas_type: Optional canvas type filter ('general', 'asset', 'storyboard')
            include_deleted: Whether to include soft-deleted canvases
            
        Returns:
            List of canvas metadata records
        """
        query = select(CanvasMetadataDB).where(
            CanvasMetadataDB.project_id == project_id
        )
        
        if canvas_type:
            query = query.where(CanvasMetadataDB.canvas_type == canvas_type)
        
        if not include_deleted:
            query = query.where(CanvasMetadataDB.deleted_at == None)
        
        # Order by order_index for general canvases, then by created_at
        query = query.order_by(
            CanvasMetadataDB.order_index, 
            CanvasMetadataDB.created_at
        )
        
        return self.session.exec(query).all()
    
    def get_canvas(self, canvas_id: str) -> Optional[CanvasMetadataDB]:
        """Get a single canvas metadata by ID.
        
        Args:
            canvas_id: Canvas ID
            
        Returns:
            Canvas metadata or None if not found
        """
        return self.session.get(CanvasMetadataDB, canvas_id)
    
    def get_asset_canvas(self, asset_id: str) -> Optional[CanvasMetadataDB]:
        """Get the canvas associated with an asset.
        
        Args:
            asset_id: Asset ID
            
        Returns:
            Canvas metadata or None if not found
        """
        return self.session.exec(
            select(CanvasMetadataDB)
            .where(CanvasMetadataDB.canvas_type == 'asset')
            .where(CanvasMetadataDB.related_entity_id == asset_id)
            .where(CanvasMetadataDB.deleted_at == None)
        ).first()
    
    def get_or_create_asset_canvas(self, asset_id: str) -> CanvasMetadataDB:
        """Get or create a canvas for an asset.
        
        If a canvas already exists for this asset, return it.
        Otherwise, create a new canvas with the asset's information.
        
        Args:
            asset_id: Asset ID
            
        Returns:
            Canvas metadata (existing or newly created)
            
        Raises:
            ValueError: If asset not found
        """
        # Check if canvas already exists
        canvas = self.get_asset_canvas(asset_id)
        if canvas:
            logger.info(f"Found existing canvas {canvas.id} for asset {asset_id}")
            return canvas
        
        # 查找 the asset
        asset = self.session.get(AssetDB, asset_id)
        if not asset:
            raise ValueError(f"Asset {asset_id} not found")
        
        logger.info(f"Creating new canvas for asset {asset_id}")
        
        # 创建 canvas metadata
        canvas = CanvasMetadataDB(
            id=str(uuid.uuid4()),
            project_id=asset.project_id,
            canvas_type='asset',
            related_entity_type='asset',
            related_entity_id=asset_id,
            name=asset.name,
            created_at=datetime.now().timestamp(),
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas)
        
        # 创建 empty canvas content
        canvas_content = CanvasDB(
            id=canvas.id,
            project_id=asset.project_id,
            nodes=[],
            connections=[],
            groups=[],
            history=[],
            history_index=-1,
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas_content)
        self.session.commit()
        self.session.refresh(canvas)
        
        logger.info(f"Created canvas {canvas.id} for asset {asset_id}")
        return canvas
    
    def get_storyboard_canvas(self, storyboard_id: str) -> Optional[CanvasMetadataDB]:
        """Get the canvas associated with a storyboard.
        
        Args:
            storyboard_id: Storyboard ID
            
        Returns:
            Canvas metadata or None if not found
        """
        return self.session.exec(
            select(CanvasMetadataDB)
            .where(CanvasMetadataDB.canvas_type == 'storyboard')
            .where(CanvasMetadataDB.related_entity_id == storyboard_id)
            .where(CanvasMetadataDB.deleted_at == None)
        ).first()
    
    def get_or_create_storyboard_canvas(self, storyboard_id: str) -> CanvasMetadataDB:
        """Get or create a canvas for a storyboard.
        
        If a canvas already exists for this storyboard, return it.
        Otherwise, create a new canvas with the storyboard's information.
        
        Args:
            storyboard_id: Storyboard ID
            
        Returns:
            Canvas metadata (existing or newly created)
            
        Raises:
            ValueError: If storyboard not found
        """
        # Check if canvas already exists
        canvas = self.get_storyboard_canvas(storyboard_id)
        if canvas:
            logger.info(f"Found existing canvas {canvas.id} for storyboard {storyboard_id}")
            return canvas
        
        # 查找 the storyboard
        storyboard = self.session.get(StoryboardDB, storyboard_id)
        if not storyboard:
            raise ValueError(f"Storyboard {storyboard_id} not found")
        
        logger.info(f"Creating new canvas for storyboard {storyboard_id}")
        
        # 创建 canvas metadata
        canvas = CanvasMetadataDB(
            id=str(uuid.uuid4()),
            project_id=storyboard.project_id,
            canvas_type='storyboard',
            related_entity_type='storyboard',
            related_entity_id=storyboard_id,
            name=storyboard.shot,
            created_at=datetime.now().timestamp(),
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas)
        
        # 创建 empty canvas content
        canvas_content = CanvasDB(
            id=canvas.id,
            project_id=storyboard.project_id,
            nodes=[],
            connections=[],
            groups=[],
            history=[],
            history_index=-1,
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas_content)
        self.session.commit()
        self.session.refresh(canvas)
        
        logger.info(f"Created canvas {canvas.id} for storyboard {storyboard_id}")
        return canvas
    
    def create_general_canvas(
        self, 
        project_id: str, 
        name: str,
        description: Optional[str] = None
    ) -> CanvasMetadataDB:
        """ 创建 a new general canvas.
        
        General canvases are free-form canvases not bound to any specific
        asset or storyboard. They are ordered by order_index.
        
        Args:
            project_id: Project ID
            name: Canvas name
            description: Optional canvas description
            
        Returns:
            Newly created canvas metadata
        """
        # 获取 the current maximum order_index for general canvases in this project
        max_order_result = self.session.exec(
            select(CanvasMetadataDB.order_index)
            .where(CanvasMetadataDB.project_id == project_id)
            .where(CanvasMetadataDB.canvas_type == 'general')
            .order_by(CanvasMetadataDB.order_index.desc())
        ).first()
        
        next_order = (max_order_result or 0) + 1
        
        logger.info(f"Creating general canvas '{name}' for project {project_id} with order {next_order}")
        
        # 创建 canvas metadata
        canvas = CanvasMetadataDB(
            id=str(uuid.uuid4()),
            project_id=project_id,
            canvas_type='general',
            name=name,
            description=description,
            order_index=next_order,
            created_at=datetime.now().timestamp(),
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas)
        
        # 创建 empty canvas content
        canvas_content = CanvasDB(
            id=canvas.id,
            project_id=project_id,
            nodes=[],
            connections=[],
            groups=[],
            history=[],
            history_index=-1,
            updated_at=datetime.now().timestamp()
        )
        
        self.session.add(canvas_content)
        self.session.commit()
        self.session.refresh(canvas)
        
        logger.info(f"Created general canvas {canvas.id}")
        return canvas
    
    def update_canvas(
        self, 
        canvas_id: str, 
        updates: dict
    ) -> Optional[CanvasMetadataDB]:
        """Update canvas metadata.
        
        Args:
            canvas_id: Canvas ID
            updates: Dictionary of fields to update
            
        Returns:
            Updated canvas metadata or None if not found
        """
        canvas = self.get_canvas(canvas_id)
        if not canvas:
            logger.warning(f"Canvas {canvas_id} not found for update")
            return None
        
        # 更新 fields
        for key, value in updates.items():
            if hasattr(canvas, key):
                setattr(canvas, key, value)
        
        # Always update the updated_at timestamp
        canvas.updated_at = datetime.now().timestamp()
        
        self.session.commit()
        self.session.refresh(canvas)
        
        logger.info(f"Updated canvas {canvas_id}")
        return canvas
    
    def delete_canvas(self, canvas_id: str, hard_delete: bool = False):
        """ 删除 a canvas (soft delete by default).
        
        Soft delete sets the deleted_at timestamp, allowing recovery.
        Hard delete permanently removes the canvas and its content.
        
        Args:
            canvas_id: Canvas ID
            hard_delete: If True, permanently delete; if False, soft delete
        """
        canvas = self.get_canvas(canvas_id)
        if not canvas:
            logger.warning(f"Canvas {canvas_id} not found for deletion")
            return
        
        if hard_delete:
            logger.info(f"Hard deleting canvas {canvas_id}")
            
            # 删除 canvas content
            canvas_content = self.session.get(CanvasDB, canvas_id)
            if canvas_content:
                self.session.delete(canvas_content)
            
            # 删除 metadata
            self.session.delete(canvas)
        else:
            logger.info(f"Soft deleting canvas {canvas_id}")
            
            # Soft delete
            canvas.deleted_at = datetime.now().timestamp()
        
        self.session.commit()
    
    def reorder_canvases(self, canvas_orders: List[dict]):
        """ 批处理 update canvas order.
        
        This is typically used for general canvases to allow users to
        reorder them in the UI.
        
        Args:
            canvas_orders: List of dicts with 'id' and 'order_index' keys
        """
        logger.info(f"Reordering {len(canvas_orders)} canvases")
        
        for item in canvas_orders:
            canvas = self.get_canvas(item['id'])
            if canvas:
                canvas.order_index = item['order_index']
                canvas.updated_at = datetime.now().timestamp()
        
        self.session.commit()
    
    def update_access_stats(self, canvas_id: str):
        """Update access statistics for a canvas.
        
        This increments the access count and updates the last accessed time.
        Useful for tracking canvas usage and implementing LRU caching.
        
        Args:
            canvas_id: Canvas ID
        """
        canvas = self.get_canvas(canvas_id)
        if canvas:
            canvas.last_accessed_at = datetime.now().timestamp()
            canvas.access_count += 1
            self.session.commit()
            logger.debug(f"Updated access stats for canvas {canvas_id}: count={canvas.access_count}")
