import asyncio
import time
import uuid
from typing import Any, Dict, List

import httpx
from monke.bongos.base_bongo import BaseBongo
from monke.generation.outlook_mail import generate_outlook_message
from monke.utils.logging import get_logger

GRAPH = "https://graph.microsoft.com/v1.0"


class OutlookMailBongo(BaseBongo):
    connector_type = "outlook_mail"

    def __init__(self, credentials: Dict[str, Any], **kwargs):
        super().__init__(credentials)
        self.access_token: str = credentials["access_token"]
        self.entity_count: int = int(kwargs.get("entity_count", 3))
        self.openai_model: str = kwargs.get("openai_model", "gpt-4.1-mini")
        self.rate_limit_delay = float(kwargs.get("rate_limit_delay_ms", 500)) / 1000.0
        self.logger = get_logger("outlook_mail_bongo")
        self._messages: List[Dict[str, Any]] = []
        self._last_req = 0.0

    async def create_entities(self) -> List[Dict[str, Any]]:
        self.logger.info(f"🥁 Creating {self.entity_count} Outlook draft messages")
        out: List[Dict[str, Any]] = []
        async with httpx.AsyncClient(base_url=GRAPH, timeout=30) as client:
            for _ in range(self.entity_count):
                await self._pace()
                token = uuid.uuid4().hex[:8]
                msg = await generate_outlook_message(self.openai_model, token)
                payload = {
                    "subject": msg.subject,
                    "body": {"contentType": "HTML", "content": msg.body_html},
                    "toRecipients": [{"emailAddress": {"address": a}} for a in msg.to],
                }
                r = await client.post(
                    "/me/messages", headers=self._hdrs(), json=payload
                )  # create DRAFT
                if r.status_code not in (200, 201):
                    self.logger.error(f"Draft create failed {r.status_code}: {r.text}")
                r.raise_for_status()
                data = r.json()
                ent = {
                    "type": "message",
                    "id": data["id"],
                    "name": msg.subject,
                    "token": token,
                    "expected_content": token,
                    "path": f"graph/messages/{data['id']}",
                }
                out.append(ent)
                self._messages.append(ent)
                self.created_entities.append({"id": data["id"], "name": msg.subject})
        return out

    async def update_entities(self) -> List[Dict[str, Any]]:
        if not self._messages:
            return []
        self.logger.info("🥁 Updating some drafts (append '[updated]' to subject)")
        async with httpx.AsyncClient(base_url=GRAPH, timeout=30) as client:
            updated = []
            for ent in self._messages[: min(3, len(self._messages))]:
                await self._pace()
                r = await client.patch(
                    f"/me/messages/{ent['id']}",
                    headers=self._hdrs(),
                    json={"subject": ent["name"] + " [updated]"},
                )
                r.raise_for_status()
                updated.append({**ent, "updated": True})
            return updated

    async def delete_entities(self) -> List[str]:
        return await self.delete_specific_entities(self._messages)

    async def delete_specific_entities(self, entities: List[Dict[str, Any]]) -> List[str]:
        self.logger.info(f"🥁 Deleting {len(entities)} Outlook drafts")
        deleted: List[str] = []
        async with httpx.AsyncClient(base_url=GRAPH, timeout=30) as client:
            for ent in entities:
                try:
                    await self._pace()
                    r = await client.delete(f"/me/messages/{ent['id']}", headers=self._hdrs())
                    if r.status_code == 204:
                        deleted.append(ent["id"])
                    else:
                        self.logger.warning(f"Delete {ent['id']} -> {r.status_code}: {r.text}")
                except Exception as e:
                    self.logger.warning(f"Delete error {ent['id']}: {e}")
        return deleted

    async def cleanup(self):
        """Comprehensive cleanup of all test emails."""
        self.logger.info("🧹 Starting comprehensive Outlook Mail cleanup")

        cleanup_stats = {"messages_deleted": 0, "errors": 0}

        try:
            # First, delete current session messages
            if self._messages:
                self.logger.info(f"🗑️ Cleaning up {len(self._messages)} current session messages")
                deleted = await self.delete_specific_entities(self._messages)
                cleanup_stats["messages_deleted"] += len(deleted)
                self._messages.clear()

            # Search for any remaining monke test emails
            await self._cleanup_orphaned_test_messages(cleanup_stats)

            self.logger.info(
                f"🧹 Cleanup completed: {cleanup_stats['messages_deleted']} messages deleted, "
                f"{cleanup_stats['errors']} errors"
            )
        except Exception as e:
            self.logger.error(f"❌ Error during comprehensive cleanup: {e}")

    async def _cleanup_orphaned_test_messages(self, stats: Dict[str, Any]):
        """Find and delete orphaned test messages from previous runs."""
        try:
            async with httpx.AsyncClient(base_url=GRAPH, timeout=30) as client:
                # Search for messages with monke test patterns
                filter_query = "startswith(subject, 'Test') or contains(subject, 'monke')"
                r = await client.get(
                    "/me/messages",
                    headers=self._hdrs(),
                    params={"$filter": filter_query, "$top": 100},
                )

                if r.status_code == 200:
                    messages = r.json().get("value", [])
                    test_messages = [
                        m
                        for m in messages
                        if any(
                            pattern in m.get("subject", "").lower()
                            for pattern in ["test", "monke", "demo", "sample"]
                        )
                    ]

                    if test_messages:
                        self.logger.info(
                            f"🔍 Found {len(test_messages)} potential test messages to clean"
                        )
                        for msg in test_messages:
                            try:
                                await self._pace()
                                del_r = await client.delete(
                                    f"/me/messages/{msg['id']}", headers=self._hdrs()
                                )
                                if del_r.status_code == 204:
                                    stats["messages_deleted"] += 1
                                    self.logger.info(
                                        f"✅ Deleted orphaned message: {msg.get('subject')}"
                                    )
                                else:
                                    stats["errors"] += 1
                            except Exception as e:
                                stats["errors"] += 1
                                self.logger.warning(f"⚠️ Failed to delete message {msg['id']}: {e}")
        except Exception as e:
            self.logger.warning(f"⚠️ Could not search for orphaned messages: {e}")

    def _hdrs(self) -> Dict[str, str]:
        return {"Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json"}

    async def _pace(self):
        now = time.time()
        if (delta := now - self._last_req) < self.rate_limit_delay:
            await asyncio.sleep(self.rate_limit_delay - delta)
        self._last_req = time.time()
