"""Send a report by email, with attachments.

Fixes over the naive version, in order of importance:

  1. The app password is read from the SMTP_PASSWORD environment variable, never
     written in the source. A password committed to git is a password to rotate.
  2. The host was '://gmail.com', which is not a hostname. Gmail's SMTP endpoint
     is smtp.gmail.com.
  3. A plain-text alternative is attached alongside the HTML, so the message does
     not look empty in clients that block HTML.
  4. --dry-run prints the message instead of sending it.

    export SMTP_PASSWORD='your app password'
    python3 report_mailer.py --to ops@example.com --subject "Weekly report" \
        --attach report.xlsx --dry-run

Standard library only.
"""

from __future__ import annotations

import argparse
import mimetypes
import os
import smtplib
import ssl
import sys
from email.message import EmailMessage
from pathlib import Path

DEFAULT_HOST = "smtp.gmail.com"
DEFAULT_PORT = 465

HTML_BODY = """\
<p>Hello,</p>
<p>{intro}</p>
<p>Regards,<br>{sender_name}</p>
"""

TEXT_BODY = """\
Hello,

{intro}

Regards,
{sender_name}
"""


def build_message(args: argparse.Namespace, sender: str) -> EmailMessage:
    message = EmailMessage()
    message["Subject"] = args.subject
    message["From"] = sender
    message["To"] = ", ".join(args.to)

    fields = {"intro": args.intro, "sender_name": args.sender_name}
    # Plain text first, then HTML as the richer alternative.
    message.set_content(TEXT_BODY.format(**fields))
    message.add_alternative(HTML_BODY.format(**fields), subtype="html")

    for path in args.attach or []:
        file_path = Path(path)
        if not file_path.is_file():
            raise FileNotFoundError(f"Attachment not found: {file_path}")
        guessed, _ = mimetypes.guess_type(file_path.name)
        maintype, _, subtype = (guessed or "application/octet-stream").partition("/")
        message.add_attachment(
            file_path.read_bytes(),
            maintype=maintype,
            subtype=subtype,
            filename=file_path.name,
        )
    return message


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Send a report by email.")
    parser.add_argument("--to", nargs="+", required=True, help="Recipient address(es)")
    parser.add_argument("--subject", default="Weekly status report")
    parser.add_argument("--intro", default="This week's report is attached.")
    parser.add_argument("--sender-name", default="Z-Deck")
    parser.add_argument("--attach", nargs="*", help="Files to attach")
    parser.add_argument("--host", default=os.getenv("SMTP_HOST", DEFAULT_HOST))
    parser.add_argument("--port", type=int, default=int(os.getenv("SMTP_PORT", DEFAULT_PORT)))
    parser.add_argument("--dry-run", action="store_true", help="Print instead of sending")
    args = parser.parse_args(argv)

    sender = os.getenv("SMTP_USER")
    password = os.getenv("SMTP_PASSWORD")

    if not sender:
        print("Set SMTP_USER to the sending address.", file=sys.stderr)
        return 1

    try:
        message = build_message(args, sender)
    except FileNotFoundError as exc:
        print(exc, file=sys.stderr)
        return 1

    if args.dry_run:
        print("--- dry run, nothing sent ---")
        print(f"host    : {args.host}:{args.port}")
        print(f"from    : {sender}")
        print(f"to      : {', '.join(args.to)}")
        print(f"subject : {args.subject}")
        attachments = [p.get_filename() for p in message.iter_attachments()]
        print(f"attached: {attachments or 'none'}")
        return 0

    if not password:
        print("Set SMTP_PASSWORD (an app password, not your account password).", file=sys.stderr)
        return 1

    context = ssl.create_default_context()
    try:
        with smtplib.SMTP_SSL(args.host, args.port, context=context) as server:
            server.login(sender, password)
            server.send_message(message)
    except smtplib.SMTPAuthenticationError:
        print("Authentication failed. Gmail requires a 16-character app password "
              "with 2FA enabled.", file=sys.stderr)
        return 1
    except (smtplib.SMTPException, OSError) as exc:
        print(f"Send failed: {exc}", file=sys.stderr)
        return 1

    print(f"Sent to {', '.join(args.to)}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
