from typing import Dict, Any, Optional
from sqlalchemy.orm import Session
from app.db import models
import logging

logger = logging.getLogger(__name__)

class PromptService:
    """
    Manages centralized system prompts, including fetching, rendering, 
    and permission validation.
    """

    def get_prompt_by_slug(self, db: Session, slug: str, user_id: str) -> Optional[models.PromptTemplate]:
        """
        Fetches a prompt template by its unique slug, verifying if the user has access.
        """
        prompt = db.query(models.PromptTemplate).filter(models.PromptTemplate.slug == slug).first()
        if not prompt:
            logger.warning(f"Prompt with slug '{slug}' not found.")
            return None

        if self._can_access_prompt(db, prompt, user_id):
            return prompt
        
        logger.warning(f"User '{user_id}' denied access to prompt '{slug}'.")
        return None

    def _can_access_prompt(self, db: Session, prompt: models.PromptTemplate, user_id: str) -> bool:
        """
        Internal logic to check if a user can access a specific prompt template.
        """
        # 1. Is it public?
        if prompt.is_public:
            return True
        
        # 2. Is the user the owner?
        if prompt.owner_id == user_id:
            return True
        
        # 3. Does the user belong to the allowed group?
        user = db.query(models.User).filter(models.User.id == user_id).first()
        if user and prompt.group_id and user.group_id == prompt.group_id:
            return True

        # 4. Check explicit granular permissions
        permission = db.query(models.AssetPermission).filter(
            models.AssetPermission.resource_type == 'prompt',
            models.AssetPermission.resource_id == prompt.id,
            (models.AssetPermission.user_id == user_id) | 
            ((models.AssetPermission.group_id == user_id) if user and user.group_id else False)
        ).first()
        
        if permission:
            return True

        return False

    def render_prompt(self, prompt: models.PromptTemplate, variables: Dict[str, Any]) -> str:
        """
        Renders a prompt template string using the provided variables.
        """
        try:
            return prompt.content.format(**variables)
        except KeyError as e:
            logger.error(f"Missing variable for prompt rendering: {e}")
            return prompt.content # Fallback to raw if rendering fails partially
        except Exception as e:
            logger.error(f"Error rendering prompt: {e}")
            return prompt.content
