from __future__ import annotations

from datetime import datetime
from email.message import EmailMessage
from email.policy import SMTP
from email.utils import format_datetime, make_msgid

from ccma.domain.mail_templates import (
    MailTemplate,
    MailTemplateError,
    render_mail_template,
    template_spec,
    validate_block_markers,
)
from ccma.domain.models import Member
from ccma.domain.placeholders import base_placeholder_values
from ccma.storage.repository import MemberRepository, RepositoryError


def template_values(
    member: Member,
    organization: dict | None,
    *,
    signature: str,
    created_at: datetime | None = None,
) -> dict[str, str]:
    values = base_placeholder_values(member, organization, created_at=created_at)
    # A mail always opens with a salutation, so an empty first name would read as
    # "Hallo ," -- every other placeholder may legitimately render empty.
    values["member.first_name"] = member.first_name.strip() or "Mitglied"
    values["signature"] = signature.strip()
    return values


def payment_instructions(
    member: Member,
    organization: dict | None,
    *,
    due_date: str,
    reference: str,
) -> str:
    """Ready-made payment paragraph: members with an active mandate are told the
    money is collected, everyone else gets the club's bank details."""
    organization = organization if isinstance(organization, dict) else {}
    if member.mandate_active:
        mandate = member.mandate_reference.strip()
        mandate_hint = f" (Mandatsreferenz {mandate})" if mandate else ""
        return (
            f"Du hast uns ein SEPA-Lastschriftmandat erteilt{mandate_hint} – wir ziehen den "
            "Betrag fristgerecht von deinem Konto ein. Du musst also nichts weiter tun; "
            "vor dem Einzug informieren wir dich rechtzeitig per E-Mail."
        )
    deadline = f" bis zum {due_date}" if due_date else ""
    lines = [f"Bitte überweise den Betrag{deadline} auf unser Vereinskonto:", ""]
    lines.append(f"IBAN: {str(organization.get('iban', '')).strip()}")
    bic = str(organization.get("bic", "")).strip()
    if bic:
        lines.append(f"BIC: {bic}")
    if reference.strip():
        lines.append(f"Verwendungszweck: {reference.strip()}")
    return "\n".join(lines)


def render_template(
    repository: MemberRepository,
    key: str,
    values: dict[str, str],
    repeats: dict[str, list[dict[str, str]]] | None = None,
) -> MailTemplate:
    template = repository.get_mail_template(key)
    try:
        # The templates are plain files in the store and may have been edited outside
        # CCMA, so the structural check runs again here: refusing to send beats
        # sending a mail with "{{#claims}}" in its text.
        validate_block_markers(key, template.subject, template.body)
        return render_mail_template(template, values, repeats)
    except MailTemplateError as exc:
        raise RepositoryError(
            f"Die E-Mail-Vorlage „{template_spec(key).label}“ ist fehlerhaft: {exc}"
        ) from exc


def compose_mail(
    *,
    recipient: str,
    subject: str,
    body: str,
    sender_name: str,
    sender_email: str,
    created_at: datetime | None = None,
) -> bytes:
    if not recipient.strip():
        raise RepositoryError("Für das Mitglied ist keine E-Mail-Adresse hinterlegt.")
    if not sender_email.strip() or "@" not in sender_email:
        raise RepositoryError("Für den Versand ist eine gültige Absenderadresse erforderlich.")
    timestamp = created_at or datetime.now().astimezone()
    message = EmailMessage(policy=SMTP)
    message["Message-ID"] = make_msgid(domain=sender_email.rsplit("@", 1)[-1])
    message["Date"] = format_datetime(timestamp)
    message["From"] = f"{sender_name.strip()} <{sender_email.strip()}>"
    message["To"] = recipient.strip()
    message["Subject"] = subject.strip()
    message["X-Mozilla-Draft-Info"] = (
        "internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0; attachmentreminder=0"
    )
    message.set_content(body.strip() + "\n", charset="utf-8")
    return message.as_bytes()
