TOOL_NAME = "email_parser"
TOOL_DESCRIPTION = """Parse email files or raw email data to extract sender, subject, date received, and body content. Supports .eml, .msg, and raw MIME format."""
TOOL_ARGS = '{"source": {"type": "string", "description": "File path to email file (.eml or .msg) or raw email content string"}, "format": {"type": "string", "enum": ["auto", "eml", "msg", "raw"], "default": "auto", "description": "Email format. Auto-detects from file extension if not specified"}, "extract_attachments": {"type": "boolean", "default": false, "description": "Whether to also extract attachment filenames"}}'

import os
import re
import email
from email import policy, utils as email_utils
from email.parser import BytesParser
from tools._std import tool_ok, tool_error

def run(args: dict) -> str:
    source = args.get("source", "")
    fmt = args.get("format", "auto")
    extract_attachments = args.get("extract_attachments", False)
    
    if not source:
        return tool_error("No source provided")
    
    # Determine format
    if fmt == "auto":
        if source.endswith(".eml") or "Content-Type:" in source[:500]:
            fmt = "eml"
        elif source.endswith(".msg"):
            fmt = "msg"
        else:
            # Try to detect if it's file content or path
            if len(source) < 500 and not source.startswith("Received:") and not source.startswith("From:"):
                fmt = "msg"  # Assume msg for short strings without email headers
            else:
                fmt = "raw"
    
    try:
        if fmt == "eml" or fmt == "raw":
            return _parse_eml(source, extract_attachments)
        elif fmt == "msg":
            return _parse_msg(source, extract_attachments)
        else:
            return tool_error(f"Unsupported format '{fmt}'")
    except Exception as e:
        return tool_error(f"Error parsing email: {str(e)}")

def _parse_eml(source, extract_attachments):
    """Parse .eml file or raw email content"""
    if os.path.exists(source):
        with open(source, 'rb') as f:
            msg = BytesParser(policy=policy.default).parse(f)
    else:
        # Treat as raw email content
        if isinstance(source, str):
            source = source.encode('utf-8', errors='ignore')
        msg = BytesParser(policy=policy.default).parsebytes(source)
    
    return _extract_email_data(msg, extract_attachments)

def _parse_msg(source, extract_attachments):
    """Parse Outlook .msg file using win32com"""
    try:
        import pythoncom
        import win32com.client
        pythoncom.CoInitialize()
        try:
            outlook = win32com.client.Dispatch("Outlook.Application")
            
            # Create temp message object
            if os.path.exists(source):
                # It's a file path
                msg = outlook.CreateItemFromTemplate(source)
            else:
                # For msg file content, we'd need to save to temp file first
                # For now, treat as unsupported for raw content
                return tool_error(".msg files must be provided as file paths, not raw content")
            
            data = {
                "sender": str(msg.Sender) if msg.Sender else msg.SenderEmailAddress,
                "subject": msg.Subject,
                "date_received": msg.ReceivedTime.strftime("%Y-%m-%d %H:%M:%S") if msg.ReceivedTime else None,
                "body": msg.Body
            }
            
            if extract_attachments:
                data["attachments"] = [att.FileName for att in msg.Attachments]
            
            return _format_output(data)
        finally:
            pythoncom.CoUninitialize()
        
    except ImportError:
        return tool_error("Parsing .msg files requires pywin32 and Outlook installed")

def _extract_email_data(msg, extract_attachments):
    """Extract data from email.message object"""
    # Get sender
    sender = msg.get("From", "")
    if sender:
        # Clean up sender format "Name <email>"
        sender_match = re.match(r'"?([^"<]+)"?\s*<([^>]+)>', sender)
        if sender_match:
            sender = f"{sender_match.group(1).strip()} <{sender_match.group(2)}>"
    
    # Get subject
    subject = msg.get("Subject", "")
    
    # Get date
    date_str = msg.get("Date", "")
    try:
        # Parse various date formats
        date_received = _parse_date(date_str)
    except Exception:
        date_received = date_str
    
    # Get body
    body = _get_body(msg)
    
    data = {
        "sender": sender,
        "subject": subject,
        "date_received": date_received,
        "body": body
    }
    
    if extract_attachments:
        attachments = []
        for part in msg.walk():
            if part.get_content_disposition() == 'attachment':
                filename = part.get_filename()
                if filename:
                    attachments.append(filename)
        data["attachments"] = attachments
    
    return _format_output(data)

def _get_body(msg):
    """Extract text body from email message"""
    if msg.is_multipart():
        # Try to get text/plain first, then text/html
        plain_body = None
        html_body = None
        
        for part in msg.walk():
            content_type = part.get_content_type()
            if content_type == "text/plain" and not plain_body:
                try:
                    plain_body = part.get_payload(decode=True).decode('utf-8', errors='ignore')
                except Exception:
                    try:
                        charset = part.get_content_charset() or 'latin-1'
                        plain_body = part.get_payload(decode=True).decode(charset, errors='ignore')
                    except Exception:
                        pass
            elif content_type == "text/html" and not html_body:
                try:
                    html_body = part.get_payload(decode=True).decode('utf-8', errors='ignore')
                except Exception:
                    try:
                        charset = part.get_content_charset() or 'latin-1'
                        html_body = part.get_payload(decode=True).decode(charset, errors='ignore')
                    except Exception:
                        pass
        
        # Prefer plain text, strip HTML if that's all we have
        if plain_body:
            return plain_body.strip()
        elif html_body:
            return _strip_html(html_body)
        else:
            return ""
    else:
        try:
            charset = msg.get_content_charset() or 'utf-8'
            body = msg.get_payload(decode=True).decode(charset, errors='ignore')
            if msg.get_content_type() == "text/html":
                body = _strip_html(body)
            return body.strip()
        except Exception as e:
            return str(msg.get_payload())

def _strip_html(html):
    """Basic HTML to text conversion"""
    text = re.sub(r'<[^>]+>', ' ', html)
    text = re.sub(r'\s+', ' ', text)
    text = re.sub(r'&nbsp;', ' ', text)
    text = re.sub(r'&lt;', '<', text)
    text = re.sub(r'&gt;', '>', text)
    text = re.sub(r'&amp;', '&', text)
    return text.strip()

def _parse_date(date_str):
    """Parse email date string to standardized format using stdlib."""
    if not date_str:
        return None
    try:
        dt = email_utils.parsedate_to_datetime(date_str)
        return dt.strftime("%Y-%m-%d %H:%M:%S")
    except Exception:
        return date_str.strip()

def _format_output(data):
    """Format output as standardized JSON"""
    return tool_ok(data, f"Parsed email from {data.get('sender', 'unknown')}")
