import base64
from pathlib import Path

from backend.config import OUTPUT_DIR, GOOGLE_API_SUPPORT, OUTLOOK_SUPPORT, _google_service, _outlook_session


def _gmail_manage_labels(action: str, label: str, message_ids: str = "") -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    service = _google_service("gmail", "v1")
    if not service:
        return "Google not authorized."
    action = (action or "").strip().lower()
    label = (label or "").strip()
    if not label:
        return "ERROR: label name is required."
    try:
        resp = service.users().labels().list(userId="me").execute()
        all_labels = resp.get("labels", [])
        label_id = None
        for existing_label in all_labels:
            if existing_label.get("name", "").lower() == label.lower():
                label_id = existing_label["id"]
                break
        if action == "create":
            if label_id:
                return f"Label '{label}' already exists."
            created = service.users().labels().create(
                userId="me", body={"name": label, "labelListVisibility": "labelShow", "messageListVisibility": "show"}
            ).execute()
            return f"Label '{created.get('name')}' created (id: {created.get('id')})."
        if action == "delete":
            if not label_id:
                return f"Label '{label}' not found."
            service.users().labels().delete(userId="me", id=label_id).execute()
            return f"Label '{label}' deleted."
        if action == "list":
            names = sorted(existing_label.get("name", "") for existing_label in all_labels if existing_label.get("type") == "user")
            return "Labels:\n" + "\n".join(f"  - {name}" for name in names) if names else "No user labels."
        if action in ("add", "move", "apply"):
            if not label_id:
                created = service.users().labels().create(
                    userId="me", body={"name": label, "labelListVisibility": "labelShow", "messageListVisibility": "show"}
                ).execute()
                label_id = created["id"]
            ids = [message_id.strip() for message_id in str(message_ids).split(",") if message_id.strip()]
            if not ids:
                return "ERROR: message_ids required to apply a label."
            body: dict = {"addLabelIds": [label_id]}
            if action == "move":
                body["removeLabelIds"] = ["INBOX"]
            service.users().messages().batchModify(userId="me", body={"ids": ids, **body}).execute()
            return f"Label '{label}' applied to {len(ids)} message(s)."
        if action == "remove":
            if not label_id:
                return f"Label '{label}' not found."
            ids = [message_id.strip() for message_id in str(message_ids).split(",") if message_id.strip()]
            if not ids:
                return "ERROR: message_ids required."
            service.users().messages().batchModify(
                userId="me", body={"ids": ids, "removeLabelIds": [label_id]}
            ).execute()
            return f"Label '{label}' removed from {len(ids)} message(s)."
        return f"Unknown label action: {action}. Use create/delete/list/add/move/remove."
    except Exception as e:
        return f"Gmail label error: {e}"


def _outlook_move_email(message_id: str, folder: str) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available."
    if not message_id:
        return "ERROR: message_id is required."
    if not folder:
        return "ERROR: folder name is required."
    try:
        with _outlook_session() as (_, ns):
            item = ns.GetItemFromID(message_id)
            target = None
            for existing_folder in ns.GetDefaultFolder(6).Folders:
                if existing_folder.Name.lower() == folder.lower():
                    target = existing_folder
                    break
            if target is None:
                root = ns.GetDefaultFolder(6)
                target = root.Folders.Add(folder)
            item.Move(target)
        return f"Email moved to '{folder}'."
    except Exception as e:
        return f"Outlook move error: {e}"


def _gmail_bulk_action(action: str, query: str, max_count: int = 50) -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    service = _google_service("gmail", "v1")
    if not service:
        return "Google not authorized."
    action = (action or "").strip().lower()
    if action not in ("archive", "delete", "read", "unread", "label"):
        return f"Unknown action '{action}'. Use archive/delete/read/unread."
    max_count = min(max_count, 200)
    try:
        resp = service.users().messages().list(userId="me", q=query, maxResults=max_count).execute()
        ids = [message["id"] for message in resp.get("messages", [])]
        if not ids:
            return f"No emails matching '{query}'."
        body: dict = {"ids": ids}
        if action == "archive":
            body["removeLabelIds"] = ["INBOX"]
        elif action == "delete":
            body["addLabelIds"] = ["TRASH"]
        elif action == "read":
            body["removeLabelIds"] = ["UNREAD"]
        elif action == "unread":
            body["addLabelIds"] = ["UNREAD"]
        service.users().messages().batchModify(userId="me", body=body).execute()
        return f"{action.title()}d {len(ids)} email(s) matching '{query}'."
    except Exception as e:
        return f"Gmail bulk error: {e}"


def _outlook_bulk_action(action: str, query: str, max_count: int = 50) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available."
    action = (action or "").strip().lower()
    max_count = min(max_count, 200)
    try:
        with _outlook_session() as (_, ns):
            inbox = ns.GetDefaultFolder(6)
            items = inbox.Items
            collected = []
            for item in items:
                try:
                    subject = str(getattr(item, "Subject", "") or "").lower()
                    sender = str(getattr(item, "SenderName", "") or "").lower()
                    if query.lower() in subject or query.lower() in sender:
                        collected.append(item)
                        if len(collected) >= max_count:
                            break
                except Exception:
                    continue
            if not collected:
                return f"No emails matching '{query}'."
            trash = ns.GetDefaultFolder(3)
            for item in collected:
                try:
                    if action == "delete":
                        item.Move(trash)
                    elif action == "archive":
                        archive = None
                        for folder in ns.GetDefaultFolder(6).Folders:
                            if folder.Name.lower() == "archive":
                                archive = folder
                                break
                        if archive:
                            item.Move(archive)
                        else:
                            item.Move(trash)
                    elif action == "read":
                        item.UnRead = False
                        item.Save()
                    elif action == "unread":
                        item.UnRead = True
                        item.Save()
                except Exception:
                    continue
        return f"{action.title()}d {len(collected)} email(s) matching '{query}'."
    except Exception as e:
        return f"Outlook bulk error: {e}"


def _gmail_get_attachments(message_id: str, download: bool = False) -> str:
    if not GOOGLE_API_SUPPORT:
        return "Google API libraries not installed."
    service = _google_service("gmail", "v1")
    if not service:
        return "Google not authorized."
    if not message_id:
        return "ERROR: message_id is required."
    try:
        msg = service.users().messages().get(userId="me", id=message_id).execute()
        parts = msg.get("payload", {}).get("parts", [])
        attachments: list[dict] = []
        for part in parts:
            filename = part.get("filename", "")
            if filename and part.get("body", {}).get("attachmentId"):
                attachments.append({
                    "filename": filename,
                    "mimeType": part.get("mimeType", ""),
                    "size": part.get("body", {}).get("size", 0),
                    "attachmentId": part["body"]["attachmentId"],
                })
        if not attachments:
            return "No attachments found on this message."
        lines = [f"Attachments ({len(attachments)}):"]
        for attachment in attachments:
            size_kb = attachment["size"] // 1024
            lines.append(f"  - {attachment['filename']} ({attachment['mimeType']}, {size_kb} KB)")
            if download:
                try:
                    data = service.users().messages().attachments().get(
                        userId="me", messageId=message_id, id=attachment["attachmentId"]
                    ).execute()
                    content = base64.urlsafe_b64decode(data["data"])
                    dest = OUTPUT_DIR / attachment["filename"]
                    dest.write_bytes(content)
                    lines.append(f"    -> saved to output/{attachment['filename']}")
                except Exception as e:
                    lines.append(f"    -> download failed: {e}")
        return "\n".join(lines)
    except Exception as e:
        return f"Gmail attachment error: {e}"


def _outlook_get_attachments(message_id: str, download: bool = False) -> str:
    if not OUTLOOK_SUPPORT:
        return "Outlook not available."
    if not message_id:
        return "ERROR: message_id is required."
    try:
        with _outlook_session() as (_, ns):
            item = ns.GetItemFromID(message_id)
            attachments = item.Attachments
            if attachments.Count == 0:
                return "No attachments found on this message."
            lines = [f"Attachments ({attachments.Count}):"]
            for index in range(1, attachments.Count + 1):
                attachment = attachments.Item(index)
                filename = attachment.FileName or f"attachment_{index}"
                size_kb = (attachment.Size or 0) // 1024
                lines.append(f"  - {filename} ({size_kb} KB)")
                if download:
                    try:
                        dest = OUTPUT_DIR / filename
                        attachment.SaveAsFile(str(dest))
                        lines.append(f"    -> saved to output/{filename}")
                    except Exception as e:
                        lines.append(f"    -> download failed: {e}")
        return "\n".join(lines)
    except Exception as e:
        return f"Outlook attachment error: {e}"