import json, os, sys, time, urllib.parse, urllib.request, urllib.error

import jwt
import base64
import mimetypes
from email import encoders
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

TOKEN_URL = "https://oauth2.googleapis.com/token"


def _cfg():
    return json.loads(os.environ.get("INTEGRATION_SECRETS", "{}"))


def _token(scopes, subject=None):
    cfg = _cfg()
    raw = cfg.get("service_account_json", "")
    sa = json.loads(raw) if isinstance(raw, str) else raw
    if not sa.get("client_email") or not sa.get("private_key"):
        raise Exception("service_account_json must contain client_email and private_key")
    now = int(time.time())
    aud = sa.get("token_uri") or TOKEN_URL
    payload = {
        "iss": sa["client_email"],
        "scope": " ".join(scopes),
        "aud": aud,
        "iat": now,
        "exp": now + 3600,
    }
    sub = subject or cfg.get("user_id") or ""
    if sub:
        payload["sub"] = sub
    assertion = jwt.encode(payload, sa["private_key"], algorithm="RS256")
    data = urllib.parse.urlencode({
        "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
        "assertion": assertion,
    }).encode("utf-8")
    req = urllib.request.Request(aud, data=data,
                                 headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST")
    with urllib.request.urlopen(req, timeout=60) as r:
        tok = json.loads(r.read())
    if not tok.get("access_token"):
        raise Exception("Token request failed: " + json.dumps(tok))
    return tok["access_token"]


def request(method, url, scopes, params=None, body=None, subject=None):
    q = {k: str(x) for k, x in (params or {}).items() if x not in (None, "")}
    if q:
        url += ("&" if "?" in url else "?") + urllib.parse.urlencode(q)
    data = json.dumps(body).encode("utf-8") if body is not None else None
    headers = {"Accept": "application/json", "Authorization": "Bearer " + _token(scopes, subject)}
    if data is not None:
        headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=90) as r:
        raw = r.read()
        return json.loads(raw) if raw else {}


SCOPES = [
    "https://www.googleapis.com/auth/gmail.compose",
    "https://www.googleapis.com/auth/gmail.send",
]


def _build_message(inputs, mailbox):
    body = str(inputs.get("body") or "")
    html_body = str(inputs.get("html_body") or "")
    att_name = str(inputs.get("attachment_name") or "")
    att_b64 = str(inputs.get("attachment_base64") or "")

    if html_body or att_b64:
        message = MIMEMultipart("mixed" if att_b64 else "alternative")
        if body:
            message.attach(MIMEText(body, "plain", "utf-8"))
        if html_body:
            message.attach(MIMEText(html_body, "html", "utf-8"))
        if att_b64:
            name = att_name or "attachment.bin"
            ctype = mimetypes.guess_type(name)[0] or "application/octet-stream"
            main_type, sub_type = ctype.split("/", 1)
            part = MIMEBase(main_type, sub_type)
            part.set_payload(base64.b64decode(att_b64))
            encoders.encode_base64(part)
            part.add_header("Content-Disposition", "attachment", filename=name)
            message.attach(part)
    else:
        message = MIMEText(body, "plain", "utf-8")

    message["From"] = mailbox
    message["To"] = str(inputs.get("to") or "")
    if inputs.get("cc"):
        message["Cc"] = str(inputs["cc"])
    if inputs.get("bcc"):
        message["Bcc"] = str(inputs["bcc"])
    message["Subject"] = str(inputs.get("subject") or "")
    if inputs.get("reply_to"):
        message["Reply-To"] = str(inputs["reply_to"])
    if inputs.get("in_reply_to"):
        message["In-Reply-To"] = str(inputs["in_reply_to"])
    if inputs.get("references"):
        message["References"] = str(inputs["references"])
    return message


def main():
    inputs = json.loads(os.environ.get("INTEGRATION_INPUTS", "{}"))
    mailbox = str(inputs.get("user_id") or _cfg().get("user_id") or "me")
    base = "https://gmail.googleapis.com/gmail/v1/users/" + urllib.parse.quote(mailbox, safe="")

    if not str(inputs.get("to") or "").strip():
        raise Exception("to is required")
    if not str(inputs.get("subject") or "").strip():
        raise Exception("subject is required")

    message = _build_message(inputs, mailbox)
    raw = base64.urlsafe_b64encode(message.as_bytes()).decode("ascii")
    res = request("POST", base + "/messages/send", SCOPES, body={"raw": raw}, subject=mailbox)
    print(json.dumps(res))


try:
    main()
except urllib.error.HTTPError as e:
    print(json.dumps({"error": "HTTP " + str(e.code), "detail": e.read().decode("utf-8", "replace")}))
    sys.exit(1)
except Exception as e:
    print(json.dumps({"error": str(e)}))
    sys.exit(1)
