"""
Email service for verification and password reset.

This module provides email functionality using Python's built-in smtplib.
All email features are opt-in via environment variables.
"""

import os
import smtplib
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime, timedelta
from html import escape as html_escape
from typing import Optional

from itsdangerous import URLSafeTimedSerializer, SignatureExpired, BadSignature
from flask import current_app, url_for

logger = logging.getLogger(__name__)

# Token expiry times
EMAIL_VERIFICATION_EXPIRY = 24 * 60 * 60  # 24 hours in seconds
PASSWORD_RESET_EXPIRY = 1 * 60 * 60  # 1 hour in seconds


def get_email_config():
    """Get email configuration from environment variables.

    Defaults are tuned for DictIA + Resend SMTP. Operators MUST set
    ``SMTP_FROM_ADDRESS`` to a domain verified in their Resend dashboard
    (e.g. ``noreply@dictia.ca``).
    """
    return {
        'enabled': os.environ.get('ENABLE_EMAIL_VERIFICATION', 'false').lower() == 'true',
        'required': os.environ.get('REQUIRE_EMAIL_VERIFICATION', 'false').lower() == 'true',
        'smtp_host': os.environ.get('SMTP_HOST', ''),
        'smtp_port': int(os.environ.get('SMTP_PORT', '587')),
        'smtp_username': os.environ.get('SMTP_USERNAME', ''),
        'smtp_password': os.environ.get('SMTP_PASSWORD', ''),
        'smtp_use_tls': os.environ.get('SMTP_USE_TLS', 'true').lower() == 'true',
        'smtp_use_ssl': os.environ.get('SMTP_USE_SSL', 'false').lower() == 'true',
        'from_address': os.environ.get('SMTP_FROM_ADDRESS', 'noreply@yourdomain.com'),
        'from_name': os.environ.get('SMTP_FROM_NAME', 'DictIA'),
    }


def is_email_verification_enabled() -> bool:
    """Check if email verification is enabled."""
    return get_email_config()['enabled']


def is_email_verification_required() -> bool:
    """Check if email verification is required for login."""
    config = get_email_config()
    return config['enabled'] and config['required']


def is_smtp_configured() -> bool:
    """Check if SMTP settings are properly configured."""
    config = get_email_config()
    return bool(config['smtp_host'] and config['smtp_username'] and config['smtp_password'])


def get_serializer(salt: str) -> URLSafeTimedSerializer:
    """Get a URL-safe timed serializer for token generation."""
    secret_key = current_app.config.get('SECRET_KEY', 'default-dev-key')
    return URLSafeTimedSerializer(secret_key, salt=salt)


def generate_verification_token(user_id: int) -> str:
    """Generate an email verification token."""
    serializer = get_serializer('email-verification')
    return serializer.dumps(user_id)


def generate_password_reset_token(user_id: int) -> str:
    """Generate a password reset token."""
    serializer = get_serializer('password-reset')
    return serializer.dumps(user_id)


def verify_email_token(token: str) -> Optional[int]:
    """
    Verify an email verification token.

    Returns the user_id if valid, None otherwise.
    """
    serializer = get_serializer('email-verification')
    try:
        user_id = serializer.loads(token, max_age=EMAIL_VERIFICATION_EXPIRY)
        return user_id
    except SignatureExpired:
        logger.warning("Email verification token expired")
        return None
    except BadSignature:
        logger.warning("Invalid email verification token")
        return None


def verify_reset_token(token: str) -> Optional[int]:
    """
    Verify a password reset token.

    Returns the user_id if valid, None otherwise.
    """
    serializer = get_serializer('password-reset')
    try:
        user_id = serializer.loads(token, max_age=PASSWORD_RESET_EXPIRY)
        return user_id
    except SignatureExpired:
        logger.warning("Password reset token expired")
        return None
    except BadSignature:
        logger.warning("Invalid password reset token")
        return None


def _send_email(to_email: str, subject: str, html_body: str, text_body: str = None) -> bool:
    """
    Send an email using SMTP.

    Returns True if successful, False otherwise.
    """
    config = get_email_config()

    if not is_smtp_configured():
        logger.error("SMTP is not configured. Cannot send email.")
        return False

    try:
        msg = MIMEMultipart('alternative')
        msg['Subject'] = subject
        msg['From'] = f"{config['from_name']} <{config['from_address']}>"
        msg['To'] = to_email

        # Add plain text version (explicit UTF-8 to prevent Q-encoding mojibake)
        if text_body:
            part1 = MIMEText(text_body, 'plain', 'utf-8')
            msg.attach(part1)

        # Add HTML version (explicit UTF-8 to prevent Q-encoding mojibake)
        part2 = MIMEText(html_body, 'html', 'utf-8')
        msg.attach(part2)

        # Connect to SMTP server
        if config['smtp_use_ssl']:
            server = smtplib.SMTP_SSL(config['smtp_host'], config['smtp_port'])
        else:
            server = smtplib.SMTP(config['smtp_host'], config['smtp_port'])
            if config['smtp_use_tls']:
                server.starttls()

        server.login(config['smtp_username'], config['smtp_password'])
        server.sendmail(config['from_address'], to_email, msg.as_string())
        server.quit()

        logger.info(f"Email sent successfully to {to_email}")
        return True

    except smtplib.SMTPAuthenticationError as e:
        logger.error(f"SMTP authentication failed: {e}")
        return False
    except smtplib.SMTPException as e:
        logger.error(f"SMTP error sending email: {e}")
        return False
    except Exception as e:
        logger.error(f"Error sending email: {e}")
        return False


def _get_email_template(content_html: str, content_text: str, subject: str) -> tuple[str, str]:
    """
    Wrap content in the DictIA branded email template.

    Header uses the DictIA brand gradient (118deg, #0062ff → #00bdd8 → #00c896)
    with a #0062ff fallback for clients that don't render gradients in inline
    styles. Footer mentions ``info@dictia.ca`` (canonical contact) and the
    Loi 25 tagline.

    Returns (html_body, text_body)
    """
    # Get the base URL for the logo. We prefer the dedicated DictIA logo
    # (logo-dictia.png) over the legacy PWA icon.
    try:
        logo_url = url_for('static', filename='img/logo-dictia.png', _external=True)
    except RuntimeError:
        # Outside of request context, use a placeholder
        logo_url = ""

    # Header: solid #0062ff fallback + linear-gradient overlay (best-effort
    # for the email clients that support inline-style gradients — Apple Mail,
    # iOS Mail, Gmail web).
    header_bg = (
        "background-color: #0062ff; "
        "background-image: linear-gradient(118deg, #0062ff 0%, #00bdd8 52%, #00c896 100%);"
    )

    html_body = f"""
<!DOCTYPE html>
<html lang="fr-CA">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #060d1a; margin: 0; padding: 0; background-color: #f7f9fc;">
    <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background-color: #f7f9fc;">
        <tr>
            <td style="padding: 40px 20px;">
                <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="600" style="max-width: 600px; margin: 0 auto;">
                    <!-- Header -->
                    <tr>
                        <td style="{header_bg} padding: 32px 40px; border-radius: 12px 12px 0 0;">
                            <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
                                <tr>
                                    <td>
                                        <!-- Logo and Brand -->
                                        <table role="presentation" cellspacing="0" cellpadding="0" border="0">
                                            <tr>
                                                <td style="vertical-align: middle; padding-right: 12px;">
                                                    <img src="{logo_url}" alt="DictIA" width="44" height="44" style="display: block; border-radius: 8px;">
                                                </td>
                                                <td style="vertical-align: middle;">
                                                    <h1 style="color: #ffffff; margin: 0; font-size: 28px; font-weight: 700; letter-spacing: -0.5px;">DictIA</h1>
                                                </td>
                                            </tr>
                                        </table>
                                    </td>
                                </tr>
                                <tr>
                                    <td style="padding-top: 8px;">
                                        <p style="color: rgba(255,255,255,0.92); margin: 0; font-size: 14px;">Transcription IA conforme Loi&nbsp;25</p>
                                    </td>
                                </tr>
                            </table>
                        </td>
                    </tr>

                    <!-- Content -->
                    <tr>
                        <td style="background-color: #ffffff; padding: 40px; border-left: 1px solid #e6ebf2; border-right: 1px solid #e6ebf2;">
                            {content_html}
                        </td>
                    </tr>

                    <!-- Footer -->
                    <tr>
                        <td style="background-color: #f7f9fc; padding: 24px 40px; border-radius: 0 0 12px 12px; border: 1px solid #e6ebf2; border-top: none;">
                            <table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
                                <tr>
                                    <td style="text-align: center;">
                                        <p style="color: #4b5563; font-size: 12px; margin: 0 0 8px 0;">
                                            Ce courriel vous est envoyé par DictIA. Pour toute question, contactez <a href="mailto:info@dictia.ca" style="color: #0062ff; text-decoration: none;">info@dictia.ca</a>.
                                        </p>
                                        <p style="color: #6b7280; font-size: 11px; margin: 0;">
                                            &copy; {datetime.utcnow().year} DictIA &mdash; Transcription IA conforme Loi&nbsp;25
                                        </p>
                                    </td>
                                </tr>
                            </table>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>
</html>
"""

    text_body = f"""
{subject}
{'=' * len(subject)}

{content_text}

---
Ce courriel vous est envoyé par DictIA — Transcription IA conforme Loi 25.
Pour toute question, contactez info@dictia.ca.
"""

    return html_body, text_body


def send_verification_email(user) -> bool:
    """
    Send a verification email to a user.

    Args:
        user: User model instance

    Returns True if email was sent successfully, False otherwise.
    """
    from src.database import db

    if not is_email_verification_enabled():
        logger.debug("Email verification is disabled")
        return False

    if not is_smtp_configured():
        logger.warning("Cannot send verification email: SMTP not configured")
        return False

    # Generate token and store it
    token = generate_verification_token(user.id)
    user.email_verification_token = token
    user.email_verification_sent_at = datetime.utcnow()
    db.session.commit()

    # Build verification URL
    verify_url = url_for('auth.verify_email', token=token, _external=True)

    # Display name preferred over username; fallback chain handles None/empty
    # name AND the schema-improbable case where username is also missing.
    # HTML body MUST escape user-controlled name to prevent stored XSS;
    # text body uses raw string (plaintext has no XSS surface).
    raw_display_name = ((getattr(user, 'name', None) or '').strip() or user.username or 'utilisateur').strip()
    display_name_html = html_escape(raw_display_name)
    display_name_text = raw_display_name

    subject = "Vérifiez votre courriel — DictIA"

    content_html = f"""
<h2 style="color: #060d1a; margin: 0 0 24px 0; font-size: 24px; font-weight: 700;">Vérifiez votre adresse courriel</h2>

<p style="color: #374151; margin: 0 0 16px 0; font-size: 16px;">Bonjour {display_name_html},</p>

<p style="color: #374151; margin: 0 0 24px 0; font-size: 16px;">
    Bienvenue chez DictIA. Pour terminer votre inscription et commencer à transcrire, vérifiez votre adresse courriel.
</p>

<div style="text-align: center; margin: 32px 0;">
    <a href="{verify_url}" style="display: inline-block; background-color: #0062ff; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-weight: 600; font-size: 16px;">Vérifier mon courriel</a>
</div>

<p style="color: #4b5563; font-size: 14px; margin: 24px 0 8px 0;">Ou copiez-collez ce lien dans votre navigateur :</p>
<p style="word-break: break-all; color: #0062ff; font-size: 14px; margin: 0; padding: 12px; background-color: #f7f9fc; border-radius: 6px;">{verify_url}</p>

<div style="margin-top: 32px; padding-top: 24px; border-top: 1px solid #e6ebf2;">
    <p style="color: #4b5563; font-size: 13px; margin: 0;">
        <strong>Ce lien expire dans 24&nbsp;heures.</strong><br>
        Si vous n'avez pas créé de compte DictIA, ignorez ce courriel.
    </p>
</div>
"""

    content_text = f"""Bonjour {display_name_text},

Bienvenue chez DictIA. Pour terminer votre inscription et commencer à transcrire, vérifiez votre adresse courriel.

Cliquez ici pour vérifier : {verify_url}

Ce lien expire dans 24 heures.

Si vous n'avez pas créé de compte DictIA, ignorez ce courriel."""

    html_body, text_body = _get_email_template(content_html, content_text, subject)
    return _send_email(user.email, subject, html_body, text_body)


def send_password_reset_email(user) -> bool:
    """
    Send a password reset email to a user.

    Args:
        user: User model instance

    Returns True if email was sent successfully, False otherwise.
    """
    from src.database import db

    if not is_smtp_configured():
        logger.warning("Cannot send password reset email: SMTP not configured")
        return False

    # Generate token and store it
    token = generate_password_reset_token(user.id)
    user.password_reset_token = token
    user.password_reset_sent_at = datetime.utcnow()
    db.session.commit()

    # Build reset URL
    reset_url = url_for('auth.reset_password', token=token, _external=True)

    # Display name preferred over username; fallback chain handles None/empty
    # name AND the schema-improbable case where username is also missing.
    # HTML body MUST escape user-controlled name to prevent stored XSS;
    # text body uses raw string (plaintext has no XSS surface).
    raw_display_name = ((getattr(user, 'name', None) or '').strip() or user.username or 'utilisateur').strip()
    display_name_html = html_escape(raw_display_name)
    display_name_text = raw_display_name

    subject = "Réinitialiser votre mot de passe — DictIA"

    content_html = f"""
<h2 style="color: #060d1a; margin: 0 0 24px 0; font-size: 24px; font-weight: 700;">Réinitialiser votre mot de passe</h2>

<p style="color: #374151; margin: 0 0 16px 0; font-size: 16px;">Bonjour {display_name_html},</p>

<p style="color: #374151; margin: 0 0 24px 0; font-size: 16px;">
    Nous avons reçu une demande de réinitialisation pour votre compte DictIA. Cliquez sur le bouton ci-dessous pour créer un nouveau mot de passe.
</p>

<div style="text-align: center; margin: 32px 0;">
    <a href="{reset_url}" style="display: inline-block; background-color: #0062ff; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-weight: 600; font-size: 16px;">Réinitialiser mon mot de passe</a>
</div>

<p style="color: #4b5563; font-size: 14px; margin: 24px 0 8px 0;">Ou copiez-collez ce lien dans votre navigateur :</p>
<p style="word-break: break-all; color: #0062ff; font-size: 14px; margin: 0; padding: 12px; background-color: #f7f9fc; border-radius: 6px;">{reset_url}</p>

<div style="margin-top: 32px; padding-top: 24px; border-top: 1px solid #e6ebf2;">
    <p style="color: #4b5563; font-size: 13px; margin: 0;">
        <strong>Ce lien expire dans 1&nbsp;heure.</strong><br>
        Si vous n'avez pas demandé de réinitialisation, ignorez ce courriel — votre mot de passe reste inchangé.
    </p>
</div>
"""

    content_text = f"""Bonjour {display_name_text},

Nous avons reçu une demande de réinitialisation pour votre compte DictIA. Cliquez sur le lien ci-dessous pour créer un nouveau mot de passe :

{reset_url}

Ce lien expire dans 1 heure.

Si vous n'avez pas demandé de réinitialisation, ignorez ce courriel — votre mot de passe reste inchangé."""

    html_body, text_body = _get_email_template(content_html, content_text, subject)
    return _send_email(user.email, subject, html_body, text_body)


def send_magic_link_email(user, magic_url: str) -> bool:
    """Send a magic-link login email (B-2.4).

    Args:
        user: User model instance (must have .email; .name preferred for display).
        magic_url: Absolute URL to the magic-link consume endpoint.

    The token itself is generated by ``src.auth.magic_link.generate_magic_link_token``
    and embedded in ``magic_url`` by the caller — this function only renders
    + sends the email. Stateless tokens (no DB column).

    Returns True if the email was sent successfully, False otherwise.
    """
    if not is_smtp_configured():
        logger.warning("Cannot send magic-link email: SMTP not configured")
        return False

    # Display name preferred over username; fallback chain handles None/empty
    # name AND the schema-improbable case where username is also missing.
    # HTML body MUST escape user-controlled name to prevent stored XSS;
    # text body uses raw string (plaintext has no XSS surface).
    raw_display_name = (
        (getattr(user, 'name', None) or '').strip()
        or user.username
        or 'utilisateur'
    ).strip()
    display_name_html = html_escape(raw_display_name)
    display_name_text = raw_display_name

    subject = "Votre lien de connexion DictIA"

    content_html = f"""
<h2 style="color: #060d1a; margin: 0 0 24px 0; font-size: 24px; font-weight: 700;">Votre lien de connexion</h2>

<p style="color: #374151; margin: 0 0 16px 0; font-size: 16px;">Bonjour {display_name_html},</p>

<p style="color: #374151; margin: 0 0 24px 0; font-size: 16px;">
    Cliquez sur le bouton ci-dessous pour vous connecter à DictIA sans mot de passe. Ce lien est à usage personnel et expire rapidement.
</p>

<div style="text-align: center; margin: 32px 0;">
    <a href="{magic_url}" style="display: inline-block; background-color: #0062ff; color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-weight: 600; font-size: 16px;">Se connecter à DictIA</a>
</div>

<p style="color: #4b5563; font-size: 14px; margin: 24px 0 8px 0;">Ou copiez-collez ce lien dans votre navigateur :</p>
<p style="word-break: break-all; color: #0062ff; font-size: 14px; margin: 0; padding: 12px; background-color: #f7f9fc; border-radius: 6px;">{magic_url}</p>

<div style="margin-top: 32px; padding-top: 24px; border-top: 1px solid #e6ebf2;">
    <p style="color: #4b5563; font-size: 13px; margin: 0;">
        <strong>Ce lien expire dans 15&nbsp;minutes.</strong><br>
        Si vous n'avez pas demandé ce lien de connexion, ignorez ce courriel — votre compte reste sécurisé.
    </p>
</div>
"""

    content_text = f"""Bonjour {display_name_text},

Cliquez sur le lien ci-dessous pour vous connecter à DictIA sans mot de passe :

{magic_url}

Ce lien expire dans 15 minutes.

Si vous n'avez pas demandé ce lien de connexion, ignorez ce courriel — votre compte reste sécurisé."""

    html_body, text_body = _get_email_template(content_html, content_text, subject)
    return _send_email(user.email, subject, html_body, text_body)


def can_resend_verification(user) -> tuple[bool, Optional[int]]:
    """
    Check if a verification email can be resent.

    Returns (can_resend, seconds_until_can_resend)
    """
    if not user.email_verification_sent_at:
        return True, None

    # Allow resend after 60 seconds
    cooldown = timedelta(seconds=60)
    time_since_last = datetime.utcnow() - user.email_verification_sent_at

    if time_since_last >= cooldown:
        return True, None

    remaining = (cooldown - time_since_last).seconds
    return False, remaining


def can_resend_password_reset(user) -> tuple[bool, Optional[int]]:
    """
    Check if a password reset email can be resent.

    Returns (can_resend, seconds_until_can_resend)
    """
    if not user.password_reset_sent_at:
        return True, None

    # Allow resend after 60 seconds
    cooldown = timedelta(seconds=60)
    time_since_last = datetime.utcnow() - user.password_reset_sent_at

    if time_since_last >= cooldown:
        return True, None

    remaining = (cooldown - time_since_last).seconds
    return False, remaining
