"""Gmail connector — imports photo/video attachments into Muleline.

Terminal auth:  python -m muleline.connectors.gmail auth
Import:         python -m muleline.connectors.gmail import <owner_id>
Status:         python -m muleline.connectors.gmail status

Requires ~/.muleline/client_secret.json (Google Cloud Desktop OAuth2 client).
"""
from __future__ import annotations

import base64
import mimetypes
import time
from pathlib import Path

SCOPES = [
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/gmail.modify",
]
CRED_DIR = Path.home() / ".muleline"
CLIENT_SECRET = CRED_DIR / "client_secret.json"
TOKEN_PATH = CRED_DIR / "gmail_token.json"

MEDIA_MIMES = {
    "image/jpeg", "image/png", "image/gif", "image/webp", "image/heic", "image/heif",
    "image/bmp", "image/tiff",
    "video/mp4", "video/quicktime", "video/x-msvideo", "video/webm", "video/3gpp",
    "video/x-matroska",
}

DOC_MIMES = {
    "application/pdf",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",  # .xlsx
    "application/vnd.ms-excel",  # .xls
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  # .docx
    "application/msword",  # .doc
    "application/vnd.openxmlformats-officedocument.presentationml.presentation",  # .pptx
    "application/vnd.ms-powerpoint",  # .ppt
    "text/csv",
    "text/plain",
    "application/rtf",
    "application/zip",
    "application/x-zip-compressed",
    "application/gzip",
    "application/json",
    "application/xml",
    "text/xml",
    "text/html",
}

AUDIO_MIMES = {
    "audio/mpeg", "audio/mp4", "audio/wav", "audio/ogg", "audio/aac",
    "audio/flac", "audio/x-m4a",
}

ALL_SUPPORTED = MEDIA_MIMES | DOC_MIMES | AUDIO_MIMES


def authenticate():
    """OAuth2 flow — opens browser, saves tokens to ~/.muleline/gmail_token.json."""
    from google_auth_oauthlib.flow import InstalledAppFlow

    CRED_DIR.mkdir(parents=True, exist_ok=True)
    if not CLIENT_SECRET.exists():
        print(f"Missing {CLIENT_SECRET}")
        print("1. Create Desktop OAuth2 client at console.cloud.google.com/apis/credentials")
        print("2. Enable Gmail API at console.cloud.google.com/apis/library/gmail.googleapis.com")
        print(f"3. Save the client JSON as {CLIENT_SECRET}")
        return None

    flow = InstalledAppFlow.from_client_secrets_file(str(CLIENT_SECRET), SCOPES)
    creds = flow.run_local_server(port=8590, open_browser=True)
    TOKEN_PATH.write_text(creds.to_json())
    print(f"Authenticated. Token saved to {TOKEN_PATH}")
    return creds


def get_credentials():
    """Load saved credentials, refresh if expired."""
    from google.auth.transport.requests import Request
    from google.oauth2.credentials import Credentials

    if not TOKEN_PATH.exists():
        return None
    creds = Credentials.from_authorized_user_file(str(TOKEN_PATH), SCOPES)
    if creds.expired and creds.refresh_token:
        creds.refresh(Request())
        TOKEN_PATH.write_text(creds.to_json())
    return creds


def get_service():
    """Get authenticated Gmail API service."""
    from googleapiclient.discovery import build
    creds = get_credentials()
    if not creds:
        return None
    return build("gmail", "v1", credentials=creds)


def _flatten_parts(payload):
    """Recursively flatten multipart MIME parts."""
    parts = []
    stack = list(payload.get("parts", []))
    while stack:
        p = stack.pop()
        if p.get("parts"):
            stack.extend(p["parts"])
        else:
            parts.append(p)
    return parts


def extract_attachments(service, message_id):
    """Extract photo/video attachments from a message. Returns list of {filename, mime_type, data, date}."""
    msg = service.users().messages().get(userId="me", id=message_id).execute()
    msg_date = int(msg.get("internalDate", 0)) / 1000

    results = []
    for part in _flatten_parts(msg.get("payload", {})):
        mime = part.get("mimeType", "")
        filename = part.get("filename", "")
        att_id = part.get("body", {}).get("attachmentId")
        if not filename or not att_id:
            continue

        if mime not in ALL_SUPPORTED:
            guessed, _ = mimetypes.guess_type(filename)
            if not guessed or guessed not in ALL_SUPPORTED:
                continue

        att = service.users().messages().attachments().get(
            userId="me", messageId=message_id, id=att_id
        ).execute()
        results.append({
            "filename": filename,
            "mime_type": mime,
            "data": base64.urlsafe_b64decode(att["data"]),
            "date": msg_date,
        })
    return results


def import_from_gmail(owner_id: str, storage, query="has:attachment", max_results=100):
    """Import photo/video attachments from Gmail into Muleline."""
    from muleline.db import get_db
    from muleline.exif import extract_metadata
    from muleline.hasher import sha256

    service = get_service()
    if not service:
        return {"error": "Not authenticated. Run: python -m muleline.connectors.gmail auth"}

    stats = {"imported": 0, "skipped_duplicate": 0, "errors": 0, "messages_scanned": 0}
    conn = get_db()

    try:
        resp = service.users().messages().list(userId="me", q=query, maxResults=max_results).execute()
        messages = resp.get("messages", [])

        for msg_ref in messages:
            stats["messages_scanned"] += 1
            try:
                attachments = extract_attachments(service, msg_ref["id"])
            except Exception:
                stats["errors"] += 1
                continue

            for att in attachments:
                data = att["data"]
                file_hash = sha256(data)

                if conn.execute("SELECT 1 FROM files WHERE file_hash=? AND owner_id=?", (file_hash, owner_id)).fetchone():
                    stats["skipped_duplicate"] += 1
                    continue

                original_name = att["filename"]
                storage_key = storage.write(f"{int(time.time())}_{original_name}", data)
                meta = extract_metadata(data)

                now = time.time()
                conn.execute(
                    """INSERT INTO files
                       (owner_id, filename, original_name, mime_type, file_size, file_hash,
                        storage_key, date_taken, gps_lat, gps_lon, width, height, created_at, updated_at)
                       VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
                    (owner_id, f"{int(now)}_{original_name}", original_name, att["mime_type"],
                     len(data), file_hash, storage_key,
                     meta.get("date_taken") or att["date"],
                     meta.get("gps_lat"), meta.get("gps_lon"),
                     meta.get("width"), meta.get("height"), now, now),
                )
                stats["imported"] += 1

                if stats["imported"] % 20 == 0:
                    conn.commit()

        conn.commit()
    except Exception as e:
        return {"error": str(e)}
    finally:
        conn.close()

    return stats


def main():
    import json
    import sys

    if len(sys.argv) < 2:
        print("Usage: python -m muleline.connectors.gmail {auth|status|import <owner_id>}")
        sys.exit(1)

    cmd = sys.argv[1]

    if cmd == "auth":
        authenticate()
    elif cmd == "status":
        creds = get_credentials()
        if creds and not creds.expired:
            svc = get_service()
            p = svc.users().getProfile(userId="me").execute()
            print(f"Authenticated: {p['emailAddress']} ({p.get('messagesTotal', '?')} messages)")
        else:
            print("Not authenticated. Run: python -m muleline.connectors.gmail auth")
    elif cmd == "import":
        owner_id = sys.argv[2] if len(sys.argv) > 2 else "default"
        query = "has:attachment"
        max_results = 100
        i = 3
        while i < len(sys.argv):
            if sys.argv[i] == "--query" and i + 1 < len(sys.argv):
                query = sys.argv[i + 1]; i += 2
            elif sys.argv[i] == "--max" and i + 1 < len(sys.argv):
                max_results = int(sys.argv[i + 1]); i += 2
            else:
                i += 1

        from muleline.config import UPLOAD_DIR
        from muleline.storage.local import LocalStorage
        result = import_from_gmail(owner_id, LocalStorage(str(UPLOAD_DIR)), query=query, max_results=max_results)
        print(json.dumps(result, indent=2))
    else:
        print(f"Unknown: {cmd}")
        sys.exit(1)


if __name__ == "__main__":
    main()
