"""
E-postutsending for TeeCup. I dag kun magic-link-innlogging.

Egne, uavhengige SMTP-credentials fra teeoff (ADR-009) -- se .env
(TEECUP_SMTP_SERVER/PORT/USER/PASS, TEECUP_FROM_EMAIL). `smtplib` er
synkron, så selve sendingen kjøres i en tråd (asyncio.to_thread) for ikke å
blokkere event loop-en -- samme mønster som teeoffs egen, fungerende
magic-link-utsending.

Håndterer BEGGE vanlige SMTP-tilkoblingsmåter siden porten ikke er antatt på
forhånd: implisitt TLS (SMTP_SSL, typisk port 465) eller STARTTLS på en
vanlig tilkobling (typisk port 587/25).
"""

import smtplib
from asyncio import to_thread
from dataclasses import dataclass
from email.message import EmailMessage
from html import escape as _esc

from .config import settings


def _send_sync(to_email: str, subject: str, body: str, html_body: str | None = None) -> None:
    msg = EmailMessage()
    msg["Subject"] = subject
    msg["From"] = settings.FROM_EMAIL
    msg["To"] = to_email
    msg.set_content(body)
    # html_body er valgfritt (2026-08-03, rundeoppsummering til midlertidige
    # spillere -- FØRSTE HTML-e-post i appen). add_alternative() gjør
    # meldingen multipart/alternative: body over forblir en ekte tekst-
    # fallback for klienter uten HTML-støtte, html_body er det klienter
    # faktisk viser når de kan.
    if html_body is not None:
        msg.add_alternative(html_body, subtype="html")

    if settings.SMTP_PORT == 465:
        with smtplib.SMTP_SSL(settings.SMTP_SERVER, settings.SMTP_PORT) as smtp:
            smtp.login(settings.SMTP_USER, settings.SMTP_PASS)
            smtp.send_message(msg)
    else:
        with smtplib.SMTP(settings.SMTP_SERVER, settings.SMTP_PORT) as smtp:
            smtp.starttls()
            smtp.login(settings.SMTP_USER, settings.SMTP_PASS)
            smtp.send_message(msg)


_TEMPLATES = {
    "nb": {
        "subject": "Logg inn på TeeCup",
        "body": (
            "Hei,\n\n"
            "Åpne denne lenken for å logge inn på TeeCup innen {minutes} minutter:\n\n"
            "{link}\n\n"
            "Fungerer ikke lenken? Lim inn denne koden manuelt i stedet:\n"
            "{token}\n\n"
            "Ba du ikke om dette, kan du se bort fra e-posten.\n"
        ),
    },
    "en": {
        "subject": "Log in to TeeCup",
        "body": (
            "Hi,\n\n"
            "Open this link to log in to TeeCup within {minutes} minutes:\n\n"
            "{link}\n\n"
            "Link not working? Paste this code in manually instead:\n"
            "{token}\n\n"
            "If you didn't request this, you can ignore this email.\n"
        ),
    },
}


async def send_magic_link_email(to_email: str, raw_token: str, locale: str = "nb") -> None:
    """Sender en klikkbar innloggingslenke (pluss selve koden som fallback).

    `locale` valideres allerede av Pydantic (Literal["nb","en"]) på vei inn i
    routeren, men faller trygt tilbake til "nb" her også hvis den skulle
    komme fra et annet kall uten den garantien.
    """
    template = _TEMPLATES.get(locale, _TEMPLATES["nb"])
    subject = template["subject"]
    link = f"{settings.PUBLIC_BASE_URL}/verify?token={raw_token}"
    body = template["body"].format(
        minutes=settings.MAGIC_LINK_MAX_AGE_MINUTES, token=raw_token, link=link
    )
    await to_thread(_send_sync, to_email, subject, body)


_TWO_FACTOR_TEMPLATES = {
    "nb": {
        "subject": "Din engangskode for TeeCup",
        "body": (
            "Hei,\n\n"
            "Engangskoden din er: {code}\n\n"
            "Den er gyldig i 5 minutter. Ba du ikke om dette, kan du se bort fra e-posten.\n"
        ),
    },
    "en": {
        "subject": "Your TeeCup one-time code",
        "body": (
            "Hi,\n\n"
            "Your one-time code is: {code}\n\n"
            "It is valid for 5 minutes. If you didn't request this, you can ignore this email.\n"
        ),
    },
}


async def send_two_factor_code_email(to_email: str, code: str, locale: str = "nb") -> None:
    """ADR-021 Beslutning C: e-post-2FA-engangskode. Samme leveringsmønster
    som magic-link (smtplib i egen tråd)."""
    template = _TWO_FACTOR_TEMPLATES.get(locale, _TWO_FACTOR_TEMPLATES["nb"])
    body = template["body"].format(code=code)
    await to_thread(_send_sync, to_email, template["subject"], body)


_INVITATION_TEMPLATES = {
    "nb": {
        "subject": "Du er invitert til {org} på TeeCup",
        "body": (
            "Hei,\n\n"
            "{inviter} har invitert deg til å bli {role} i {org} på TeeCup.\n\n"
            "Logg inn med denne e-postadressen på {link} for å godta invitasjonen "
            "-- den kobles automatisk til kontoen din ved innlogging.\n"
        ),
    },
    "en": {
        "subject": "You're invited to {org} on TeeCup",
        "body": (
            "Hi,\n\n"
            "{inviter} has invited you to become {role} of {org} on TeeCup.\n\n"
            "Log in with this email address at {link} to accept the invitation "
            "-- it's linked to your account automatically on login.\n"
        ),
    },
}

_ROLE_LABELS_NB = {"owner": "eier", "admin": "administrator", "member": "medlem"}
_ROLE_LABELS_EN = {"owner": "an owner", "admin": "an administrator", "member": "a member"}


async def send_organization_invitation_email(
    to_email: str, org_name: str, role: str, inviter_name: str, locale: str = "nb"
) -> None:
    """ADR-022 Beslutning B: ingen klikkbar aksept-lenke -- invitasjonen
    godtas automatisk ved neste innlogging med matchende e-post (samme
    mønster som spiller-e-post-kobling, ADR-017)."""
    template = _INVITATION_TEMPLATES.get(locale, _INVITATION_TEMPLATES["nb"])
    role_label = (_ROLE_LABELS_NB if locale == "nb" else _ROLE_LABELS_EN).get(role, role)
    subject = template["subject"].format(org=org_name)
    body = template["body"].format(
        inviter=inviter_name, role=role_label, org=org_name, link=settings.PUBLIC_BASE_URL
    )
    await to_thread(_send_sync, to_email, subject, body)


_EMAIL_CHANGE_TEMPLATES = {
    "nb": {
        "subject": "Bekreft ny e-postadresse for TeeCup",
        "body": (
            "Hei,\n\n"
            "Noen ba om å endre TeeCup-kontoens e-postadresse til denne adressen. "
            "Åpne lenken under innen {minutes} minutter for å bekrefte:\n\n"
            "{link}\n\n"
            "Ba du ikke om dette selv, kan du se bort fra e-posten -- ingenting "
            "endres uten at lenken åpnes.\n"
        ),
    },
    "en": {
        "subject": "Confirm your new TeeCup email address",
        "body": (
            "Hi,\n\n"
            "Someone requested changing this TeeCup account's email address to this "
            "address. Open the link below within {minutes} minutes to confirm:\n\n"
            "{link}\n\n"
            "If you didn't request this yourself, you can ignore this email -- "
            "nothing changes unless the link is opened.\n"
        ),
    },
}


async def send_email_change_confirmation(to_email: str, raw_token: str, locale: str = "nb") -> None:
    """ADR-031-oppfølging: e-post er innloggings-identifikatoren, kan derfor
    ikke endres med en enkel PATCH -- den NYE adressen må bevise eierskap
    FØR selve kontoen endres. Samme token-i-lenke-mønster som magic-link."""
    template = _EMAIL_CHANGE_TEMPLATES.get(locale, _EMAIL_CHANGE_TEMPLATES["nb"])
    link = f"{settings.PUBLIC_BASE_URL}/verify-email?token={raw_token}"
    body = template["body"].format(minutes=settings.MAGIC_LINK_MAX_AGE_MINUTES, link=link)
    await to_thread(_send_sync, to_email, template["subject"], body)


_SECONDARY_EMAIL_TEMPLATES = {
    "nb": {
        "subject": "Bekreft ekstra e-postadresse for TeeCup",
        "body": (
            "Hei,\n\n"
            "Noen la til denne adressen som en EKSTRA (sekundær) e-postadresse på "
            "en TeeCup-konto -- kontoens hovedadresse endres IKKE. Åpne lenken "
            "under innen {minutes} minutter for å bekrefte at du eier denne "
            "adressen:\n\n"
            "{link}\n\n"
            "Ba du ikke om dette selv, kan du se bort fra e-posten -- ingenting "
            "legges til uten at lenken åpnes.\n"
        ),
    },
    "en": {
        "subject": "Confirm an additional TeeCup email address",
        "body": (
            "Hi,\n\n"
            "Someone added this address as an ADDITIONAL (secondary) email on a "
            "TeeCup account -- the account's primary address is NOT changed. Open "
            "the link below within {minutes} minutes to confirm you own this "
            "address:\n\n"
            "{link}\n\n"
            "If you didn't request this yourself, you can ignore this email -- "
            "nothing is added unless the link is opened.\n"
        ),
    },
}


async def send_secondary_email_verification(to_email: str, raw_token: str, locale: str = "nb") -> None:
    """Én person, flere e-postadresser (notert i FEATURE_BACKLOG.md) --
    kun det enkle tilfellet (fri, ukrevd adresse). Samme
    token-i-lenke-mønster som send_email_change_confirmation, egen mal
    siden budskapet er reelt forskjellig ("legges til" vs. "endres til")."""
    template = _SECONDARY_EMAIL_TEMPLATES.get(locale, _SECONDARY_EMAIL_TEMPLATES["nb"])
    link = f"{settings.PUBLIC_BASE_URL}/verify-email?token={raw_token}&kind=secondary"
    body = template["body"].format(minutes=settings.MAGIC_LINK_MAX_AGE_MINUTES, link=link)
    await to_thread(_send_sync, to_email, template["subject"], body)


_NOTIFICATION_TEMPLATES = {
    "nb": {
        "subject": "Nytt varsel fra TeeCup",
        "body": (
            "Hei,\n\n"
            "{message}\n\n"
            "Se det i TeeCup:\n{link}\n\n"
            "Du får denne e-posten fordi du har valgt å motta e-post for "
            "denne typen varsler. Du kan endre dette når som helst under "
            "Konto i TeeCup.\n"
        ),
    },
    "en": {
        "subject": "New notification from TeeCup",
        "body": (
            "Hi,\n\n"
            "{message}\n\n"
            "View it in TeeCup:\n{link}\n\n"
            "You're receiving this because you opted in to email for this "
            "notification type. You can change this anytime under Account "
            "in TeeCup.\n"
        ),
    },
}


async def send_notification_email(to_email: str, message: str, link_path: str, locale: str = "nb") -> None:
    """E-post-fallback for det in-app varslingssenteret (FEATURE_BACKLOG.md,
    2026-07-28 oppfølging) -- kun sendt når mottakeren selv har valgt inn
    for akkurat DENNE varseltypen (`user_notification_email_pref`,
    migrasjon 033), se `create_notification()` i
    `app/routers/notifications.py`. `message` er allerede en ferdig, norsk
    tekst (samme snapshot-prinsipp som selve in-app-varselet) -- kun
    ramme-teksten (emne/hilsen/lenkeforklaring) er tospråklig her, ikke
    selve budskapet."""
    template = _NOTIFICATION_TEMPLATES.get(locale, _NOTIFICATION_TEMPLATES["nb"])
    link = f"{settings.PUBLIC_BASE_URL}{link_path}"
    body = template["body"].format(message=message, link=link)
    await to_thread(_send_sync, to_email, template["subject"], body)


_SESSION_INVITATION_TEMPLATES = {
    "nb": {
        "subject": "Din runde i {tournament} er registrert i TeeCup",
        "body": (
            "Hei {name},\n\n"
            "{summary}\n\n"
            "Åpne lenken under innen {minutes} minutter for å logge deg rett inn og se mer, "
            "eller følge med videre:\n\n"
            "{link}\n\n"
            "Fungerer ikke lenken? Lim inn denne koden manuelt i stedet:\n"
            "{token}\n\n"
            "Fikk du denne uventet? Arrangøren av turneringen har registrert deg som spiller "
            "i TeeCup -- se bort fra e-posten hvis dette ikke stemmer.\n"
        ),
    },
    "en": {
        "subject": "Your round in {tournament} has been recorded in TeeCup",
        "body": (
            "Hi {name},\n\n"
            "{summary}\n\n"
            "Open the link below within {minutes} minutes to log straight in and see more, "
            "or keep following along:\n\n"
            "{link}\n\n"
            "Link not working? Paste this code in manually instead:\n"
            "{token}\n\n"
            "Got this unexpectedly? The tournament organizer registered you as a player in "
            "TeeCup -- ignore this email if that's not right.\n"
        ),
    },
}


async def send_session_result_email(
    to_email: str, player_name: str, tournament_name: str, summary: str, raw_token: str, locale: str = "nb"
) -> None:
    """FEATURE_BACKLOG.md "Midlertidige spillere + automatisk etter-runde-
    invitasjon" (avklart 2026-07-28, økt-nivå) -- en organisator sender
    dette til spillere UTEN egen konto ennå (player.user_id IS NULL) etter
    en økt. `summary` er en allerede ferdig, norsk tekst (samme snapshot-
    prinsipp som varsel-meldinger) som beskriver matchresultatet -- bygget
    av kalleren (app/routers/tournaments.py), ikke her. Samme ekte
    klikkbare magic-link-mønster som send_magic_link_email (ikke bare en
    "logg inn senere"-henvisning som organisasjonsinvitasjonen, siden
    dette er en engangs, direkte innloggingslenke)."""
    template = _SESSION_INVITATION_TEMPLATES.get(locale, _SESSION_INVITATION_TEMPLATES["nb"])
    link = f"{settings.PUBLIC_BASE_URL}/verify?token={raw_token}"
    subject = template["subject"].format(tournament=tournament_name)
    body = template["body"].format(
        name=player_name,
        summary=summary,
        minutes=settings.MAGIC_LINK_MAX_AGE_MINUTES,
        link=link,
        token=raw_token,
    )
    await to_thread(_send_sync, to_email, subject, body)


async def send_new_account_alert_email(to_email: str, new_user_email: str, display_name: str) -> None:
    """Driftsvarsel til appens eier (2026-07-30) hver gang en HELT NY
    TeeCup-konto opprettes (app/routers/auth.py sin verify_magic_link --
    den ENESTE plassen en app_user-rad noensinne settes inn). Alltid
    norsk -- et internt driftsvarsel til én fast, kjent mottaker, ikke en
    brukervendt i18n-tekst."""
    subject = "Ny TeeCup-konto opprettet"
    body = (
        f"Hei,\n\n"
        f"En ny konto ble nettopp opprettet i TeeCup:\n\n"
        f"E-post: {new_user_email}\n"
        f"Navn: {display_name}\n"
    )
    await to_thread(_send_sync, to_email, subject, body)


@dataclass(frozen=True)
class RoundSummaryHole:
    hole_number: int
    par: int
    score: int | None
    net: int | None


async def send_round_summary_email(
    to_email: str,
    guest_first_name: str,
    round_label: str,
    holes: list[RoundSummaryHole],
    stat_lines: list[tuple[str, str]],
    raw_token: str,
) -> None:
    """Rundeoppsummering til en midlertidig spiller (gjest) med registrert
    e-post, sendt ved fullføring (app/routers/rounds.py sin complete_round,
    2026-08-03). FØRSTE HTML-e-post i appen -- multipart/alternative via
    _send_sync sin nye html_body-parameter, med en ekte tekst-fallback.

    Navneformat (CLAUDE.md, ufravikelig): dette ER direkte adressering til
    mottakeren -- KUN fornavn i hilsenen, aldri fullt navn.

    `stat_lines` er allerede ferdig utledet og formatert av kalleren (samme
    "bygg teksten i routeren, ikke i e-post-modulen"-mønster som
    send_scorecard_invitations/send_session_result_email) -- graderer seg
    naturlig etter hva som faktisk ble spilt (stat_level, individuell-ball
    vs. delt-ball) siden kalleren rett og slett utelater linjer det ikke
    finnes data for.

    Alltid norsk, samme presedens som send_scorecard_invitations (gjesten
    har ingen lagret språkpreferanse -- ingen konto ennå)."""
    link = f"{settings.PUBLIC_BASE_URL}/verify?token={raw_token}"
    subject = f"Scorekortet ditt fra {round_label}"

    text_lines = [
        f"Hei {guest_first_name},",
        "",
        f"Her er scorekortet ditt fra {round_label}:",
        "",
    ]
    for h in holes:
        score_txt = str(h.score) if h.score is not None else "–"
        net_txt = str(h.net) if h.net is not None else "–"
        text_lines.append(f"Hull {h.hole_number} (par {h.par}): {score_txt} slag (netto {net_txt})")
    text_lines.append("")
    for label, value in stat_lines:
        text_lines.append(f"{label}: {value}")
    text_lines += [
        "",
        "Noen registrerte deg som spiller i TeeCup med denne e-postadressen -- "
        "du trenger ingen konto for å se dette scorekortet, men med én kan du "
        "føre score selv, følge egen statistikk og handicap-utvikling over tid.",
        "",
        f"Logg inn og se hele runden ({settings.MAGIC_LINK_MAX_AGE_MINUTES} minutter):",
        link,
        "",
        "Ikke interessert? Ignorer denne e-posten -- den brukes ikke til noe annet.",
    ]
    body = "\n".join(text_lines)

    hole_rows_html = "\n".join(
        f'<tr><td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;">{h.hole_number}</td>'
        f'<td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;">{h.par}</td>'
        f'<td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;">{h.score if h.score is not None else "–"}</td>'
        f'<td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;">{h.net if h.net is not None else "–"}</td></tr>'
        for h in holes
    )
    stat_rows_html = "\n".join(
        f'<tr><td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;color:#424941;">{_esc(label)}</td>'
        f'<td style="padding:6px 10px;border-bottom:1px solid #e4ebe3;font-weight:700;">{_esc(value)}</td></tr>'
        for label, value in stat_lines
    )
    html_body = f"""\
<!doctype html>
<html><body style="margin:0;padding:0;background-color:#f3f6f2;font-family:Arial,Helvetica,sans-serif;color:#012c11;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f3f6f2;padding:24px 0;">
<tr><td align="center">
<table role="presentation" width="100%" style="max-width:480px;background-color:#ffffff;border-radius:16px;overflow:hidden;">
<tr><td style="background-color:#1a4325;padding:20px 24px;">
<span style="color:#ffffff;font-size:20px;font-weight:800;">TeeCup</span>
</td></tr>
<tr><td style="padding:24px;">
<p style="margin:0 0 16px;font-size:16px;">Hei {_esc(guest_first_name)},</p>
<p style="margin:0 0 20px;font-size:16px;">Her er scorekortet ditt fra <strong>{_esc(round_label)}</strong>:</p>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="font-size:14px;margin-bottom:20px;">
<tr style="background-color:#edf3ef;font-weight:700;">
<td style="padding:6px 10px;">Hull</td><td style="padding:6px 10px;">Par</td>
<td style="padding:6px 10px;">Slag</td><td style="padding:6px 10px;">Netto</td>
</tr>
{hole_rows_html}
</table>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="font-size:14px;margin-bottom:24px;">
{stat_rows_html}
</table>
<p style="margin:0 0 20px;font-size:14px;color:#424941;">
Noen registrerte deg som spiller i TeeCup med denne e-postadressen -- du trenger ingen
konto for å se dette scorekortet, men med én kan du føre score selv, følge egen
statistikk og handicap-utvikling over tid.
</p>
<table role="presentation" cellpadding="0" cellspacing="0"><tr><td style="border-radius:12px;background-color:#1f6b08;">
<a href="{link}" style="display:inline-block;padding:14px 28px;color:#ffffff;font-size:16px;
font-weight:700;text-decoration:none;">Logg inn og se hele runden</a>
</td></tr></table>
<p style="margin:20px 0 0;font-size:12px;color:#767f75;">
Lenken er gyldig i {settings.MAGIC_LINK_MAX_AGE_MINUTES} minutter. Ikke interessert? Ignorer denne
e-posten -- den brukes ikke til noe annet.
</p>
</td></tr>
</table>
</td></tr>
</table>
</body></html>
"""
    await to_thread(_send_sync, to_email, subject, body, html_body)
