# Copyright (c) 2025 Patrick Motsch
# All rights reserved.

import logging
import json
import requests
from typing import Dict, Any
from modules.datamodels.datamodelChat import ActionResult, ActionDocument

logger = logging.getLogger(__name__)

async def searchEmails(self, parameters: Dict[str, Any]) -> ActionResult:
    try:
        connectionReference = parameters.get("connectionReference")
        query = parameters.get("query")
        folder = parameters.get("folder", "All")
        limit = parameters.get("limit", 1000)
        outputMimeType = parameters.get("outputMimeType", "application/json")
        
        # Validate parameters
        if not connectionReference:
            return ActionResult.isFailure(error="Connection reference is required")
        
        # Validate limit parameter
        if limit <= 0:
            limit = 1000
            logger.warning(f"Invalid limit value ({limit}), using default value 1000")
        
        if not query or not query.strip():
            return ActionResult.isFailure(error="Search query is required and cannot be empty")
        
        # Check if this is a folder specification query
        if query.strip().lower().startswith('folder:'):
            folder_name = query.strip()[7:].strip()  # Remove "folder:" prefix
            if not folder_name:
                return ActionResult.isFailure(error="Invalid folder specification. Use format 'folder:FolderName'")
            logger.info(f"Search query is a folder specification: {folder_name}")
        
        # Validate limit
        try:
            limit = int(limit)
            if limit <= 0:
                limit = 1000
                logger.warning(f"Invalid limit value (<=0), using default value 1000")
            elif limit > 1000:  # Microsoft Graph API has limits
                limit = 1000
                logger.warning(f"Limit {limit} exceeds maximum (1000), using 1000")
        except (ValueError, TypeError):
            limit = 1000
            logger.warning(f"Invalid limit value, using default value 1000")
        
        # Get Microsoft connection
        connection = self.connection.getMicrosoftConnection(connectionReference)
        if not connection:
            return ActionResult.isFailure(error="No valid Microsoft connection found for the provided connection reference")
        
        # Search emails using Microsoft Graph API
        try:
            # Microsoft Graph API endpoint for searching messages
            graph_url = "https://graph.microsoft.com/v1.0"
            headers = {
                "Authorization": f"Bearer {connection['accessToken']}",
                "Content-Type": "application/json"
            }
            
            # Get the folder ID for the specified folder if needed
            folder_id = None
            if folder and folder.lower() != "all":
                folder_id = self.folderManagement.getFolderId(folder, connection)
                if folder_id:
                    logger.debug(f"Found folder ID for '{folder}': {folder_id}")
                else:
                    logger.warning(f"Could not find folder ID for '{folder}', using folder name directly")
            
            # Build the search API request
            api_url = f"{graph_url}/me/messages"
            params = self.emailProcessing.buildSearchParameters(query, folder_id or folder, limit)
            
            # Log search parameters for debugging
            logger.debug(f"Search query: '{query}'")
            logger.debug(f"Search folder: '{folder}'")
            logger.debug(f"Search parameters: {params}")
            logger.debug(f"API URL: {api_url}")
            
            # Make the API call
            response = requests.get(api_url, headers=headers, params=params)
            
            # Log response details for debugging

            
            if response.status_code != 200:
                # Log detailed error information
                try:
                    error_data = response.json()
                    logger.error(f"Microsoft Graph API error: {response.status_code} - {error_data}")
                except:
                    logger.error(f"Microsoft Graph API error: {response.status_code} - {response.text}")
                
                # Check for specific error types and provide helpful messages
                if response.status_code == 400:
                    logger.error("Bad Request (400) - Check search query format and parameters")
                    logger.error(f"Search query: '{query}'")
                    logger.error(f"Search parameters: {params}")
                    logger.error(f"API URL: {api_url}")
                elif response.status_code == 401:
                    logger.error("Unauthorized (401) - Check access token and permissions")
                elif response.status_code == 403:
                    logger.error("Forbidden (403) - Check API permissions and scopes")
                elif response.status_code == 429:
                    logger.error("Too Many Requests (429) - Rate limit exceeded")
                
                raise Exception(f"Microsoft Graph API returned {response.status_code}: {response.text}")
            
            response.raise_for_status()
            
            search_data = response.json()
            emails = search_data.get("value", [])
            

            
            # Apply folder filtering if needed and we used $search
            if folder and folder.lower() != "all" and "$search" in params:
                # Get the actual folder ID for proper filtering
                folder_id = self.folderManagement.getFolderId(folder, connection)
                
                if folder_id:
                    # Filter results by folder ID
                    filtered_emails = []
                    for email in emails:
                        if email.get("parentFolderId") == folder_id:
                            filtered_emails.append(email)
                    emails = filtered_emails
                    logger.debug(f"Applied folder filtering: {len(filtered_emails)} emails found in folder {folder}")
                else:
                    # Fallback: try to filter by folder name (less reliable)
                    filtered_emails = []
                    for email in emails:
                        # Check if email has folder information
                        if hasattr(email, 'parentFolderId') and email.get('parentFolderId'):
                            if email.get('parentFolderId') == folder:
                                filtered_emails.append(email)
                        else:
                            # If no folder info, include the email (less strict filtering)
                            filtered_emails.append(email)
            
                    emails = filtered_emails
                    logger.debug(f"Applied fallback folder filtering: {len(filtered_emails)} emails found in folder {folder}")
            
            # Special handling for folder specification queries
            if query.strip().lower().startswith('folder:'):
                folder_name = query.strip()[7:].strip()
                folder_id = self.folderManagement.getFolderId(folder_name, connection)
                if folder_id:
                    # Filter results to only include emails from the specified folder
                    filtered_emails = []
                    for email in emails:
                        if email.get("parentFolderId") == folder_id:
                            filtered_emails.append(email)
                    emails = filtered_emails
                    logger.debug(f"Applied folder specification filtering: {len(filtered_emails)} emails found in folder {folder_name}")
                else:
                    logger.warning(f"Could not find folder ID for folder specification: {folder_name}")
    
            
            search_result = {
                "query": query,
                "results": emails,
                "count": len(emails),
                "folder": folder,
                "limit": limit,
                "apiMetadata": {
                    "@odata.context": search_data.get("@odata.context"),
                    "@odata.count": search_data.get("@odata.count"),
                    "@odata.nextLink": search_data.get("@odata.nextLink")
                },
                "searchParams": params
            }
            

            
        except ImportError:
            logger.error("requests module not available")
            return ActionResult.isFailure(error="requests module not available")
        except Exception as e:
            logger.error(f"Error searching emails via Microsoft Graph API: {str(e)}")
            return ActionResult.isFailure(error=f"Failed to search emails: {str(e)}")
        
        # Determine output format based on MIME type
        mime_type_mapping = {
            "application/json": ".json",
            "text/plain": ".txt", 
            "text/csv": ".csv"
        }
        output_extension = mime_type_mapping.get(outputMimeType, ".json")
        output_mime_type = outputMimeType
        logger.info(f"Using output format: {output_extension} ({output_mime_type})")
        

        
        result_data = {
            "connectionReference": connectionReference,
            "query": query,
            "folder": folder,
            "limit": limit,
            "searchResults": search_result,
            "connection": {
                "id": connection["id"],
                "authority": "microsoft",
                "reference": connectionReference
            },
            "timestamp": self.services.utils.timestampGetUtc()
        }
        
        validationMetadata = {
            "actionType": "outlook.searchEmails",
            "connectionReference": connectionReference,
            "query": query,
            "folder": folder,
            "limit": limit,
            "resultCount": search_result.get("count", 0),
            "outputMimeType": outputMimeType
        }
        
        return ActionResult(
            success=True,
            documents=[ActionDocument(
                documentName=f"outlook_email_search_{self._format_timestamp_for_filename()}.json",
                documentData=json.dumps(result_data, indent=2),
                mimeType="application/json",
                validationMetadata=validationMetadata
            )]
        )
        
    except Exception as e:
        logger.error(f"Error searching emails: {str(e)}")
        return ActionResult.isFailure(error=str(e))

