"""Central email dispatch for Megawatt ERP.

Module mapping (all outbound mail should go through these dispatchers):
- system_administration: staff signup -> dispatch_signup_mail
- megawatt_agents: agent signup -> dispatch_signup_mail(agent=True)
- sales_and_marketing: customer/agent user signup -> dispatch_signup_mail
- human_resource: leave notifications -> dispatch_general_mail
- human_resource: payslip PDFs -> dispatch_attachment_mail
- marketing: campaign compose -> dispatch_marketing_mail
- all modules: exception alerts -> dispatch_error_notification
"""
import logging
from typing import Iterable, Optional
from django.core.mail import EmailMessage, get_connection, send_mail

from system_administration.models import CompanyEmailSettings, CompanyProfile

logger = logging.getLogger(__name__)

DEFAULT_ERROR_RECIPIENTS = ['bwayara21@gmail.com']


def parse_staff_date(value):
    """Accept dd/mm/yyyy (Flutter/Vue legacy) or ISO yyyy-mm-dd."""
    from datetime import datetime

    if value is None:
        return None
    text = str(value).strip()
    if not text:
        return None
    for fmt in ('%d/%m/%Y', '%Y-%m-%d', '%m/%d/%Y'):
        try:
            return datetime.strptime(text, fmt).strftime('%Y-%m-%d')
        except ValueError:
            continue
    raise ValueError(f'Invalid date format: {value}. Use dd/mm/yyyy or yyyy-mm-dd.')


def resolve_company_profile(request=None, serial_number=None):
    serial = serial_number
    if not serial and request is not None:
        data = getattr(request, 'data', None) or {}
        serial = data.get('serial_number') if isinstance(data, dict) else None
    if serial:
        try:
            return CompanyProfile.objects.get(company_serial_number=serial, recycle_bin=False)
        except CompanyProfile.DoesNotExist:
            pass
    return CompanyProfile.objects.filter(recycle_bin=False).order_by('-id').first()


def get_or_create_email_settings(company_profile: CompanyProfile) -> CompanyEmailSettings:
    settings_obj, _created = CompanyEmailSettings.objects.get_or_create(
        company_profile=company_profile,
    )
    return settings_obj


def email_settings_to_map(settings_obj: CompanyEmailSettings) -> dict:
    return {
        'smtp_host': settings_obj.smtp_host or '',
        'smtp_port': settings_obj.smtp_port,
        'smtp_use_tls': settings_obj.smtp_use_tls,
        'smtp_use_ssl': settings_obj.smtp_use_ssl,
        'smtp_username': settings_obj.smtp_username or '',
        'smtp_password_set': bool(settings_obj.smtp_password),
        'default_from_email': settings_obj.default_from_email or '',
        'signup_from_email': settings_obj.signup_from_email or '',
        'agent_signup_from_email': settings_obj.agent_signup_from_email or '',
        'enable_error_notifications': settings_obj.enable_error_notifications,
        'enable_general_notifications': settings_obj.enable_general_notifications,
        'enable_signup_notifications': settings_obj.enable_signup_notifications,
        'enable_attachment_emails': settings_obj.enable_attachment_emails,
        'enable_marketing_emails': settings_obj.enable_marketing_emails,
        'marketing_from_email': settings_obj.marketing_from_email or '',
        'error_notification_recipients': settings_obj.error_notification_recipients or '',
    }


def _parse_recipients(raw: str, fallback: Iterable[str]) -> list[str]:
    if raw and str(raw).strip():
        return [part.strip() for part in str(raw).split(',') if part.strip()]
    return list(fallback)


def _mail_connection(settings_obj: CompanyEmailSettings):
    if not settings_obj.smtp_host:
        return None
    return get_connection(
        backend='django.core.mail.backends.smtp.EmailBackend',
        host=settings_obj.smtp_host,
        port=settings_obj.smtp_port,
        username=settings_obj.smtp_username or None,
        password=settings_obj.smtp_password or None,
        use_tls=settings_obj.smtp_use_tls,
        use_ssl=settings_obj.smtp_use_ssl,
        fail_silently=False,
    )


def send_configured_mail(
    *,
    subject: str,
    message: str,
    recipient_list: list[str],
    from_email: Optional[str] = None,
    company_profile: Optional[CompanyProfile] = None,
    request=None,
    enabled: bool = True,
) -> bool:
    if not enabled:
        logger.info('Email skipped (disabled in settings): %s', subject)
        return False
    if not recipient_list:
        logger.warning('Email skipped (no recipients): %s', subject)
        return False

    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        logger.warning('Email skipped (no company profile): %s', subject)
        return False

    settings_obj = get_or_create_email_settings(company)
    sender = from_email or settings_obj.default_from_email or settings_obj.smtp_username
    if not sender:
        logger.warning('Email skipped (no from address configured): %s', subject)
        return False

    try:
        connection = _mail_connection(settings_obj)
        send_mail(
            subject,
            message,
            sender,
            recipient_list,
            connection=connection,
            fail_silently=False,
        )
        return True
    except Exception:
        logger.exception('Failed to send email: %s', subject)
        return False


def dispatch_error_notification(message, company_profile=None, request=None) -> bool:
    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        return False
    settings_obj = get_or_create_email_settings(company)
    recipients = _parse_recipients(
        settings_obj.error_notification_recipients,
        DEFAULT_ERROR_RECIPIENTS,
    )
    return send_configured_mail(
        subject='System error message',
        message=message,
        recipient_list=recipients,
        from_email=settings_obj.default_from_email,
        company_profile=company,
        enabled=settings_obj.enable_error_notifications,
    )


def dispatch_signup_mail(message, recipient_list, *, agent=False, company_profile=None, request=None) -> bool:
    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        return False
    settings_obj = get_or_create_email_settings(company)
    from_email = settings_obj.agent_signup_from_email if agent else settings_obj.signup_from_email
    return send_configured_mail(
        subject='Successfully registered',
        message=message,
        recipient_list=recipient_list,
        from_email=from_email or settings_obj.default_from_email,
        company_profile=company,
        enabled=settings_obj.enable_signup_notifications,
    )


def dispatch_general_mail(subject, message, recipient_list, company_profile=None, request=None) -> bool:
    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        return False
    settings_obj = get_or_create_email_settings(company)
    return send_configured_mail(
        subject=subject,
        message=message,
        recipient_list=recipient_list,
        from_email=settings_obj.default_from_email,
        company_profile=company,
        enabled=settings_obj.enable_general_notifications,
    )


def dispatch_attachment_mail(subject, body, to_emails, file_name, uploaded_pdf_content, company_profile=None, request=None) -> bool:
    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        return False
    settings_obj = get_or_create_email_settings(company)
    if not settings_obj.enable_attachment_emails:
        logger.info('Attachment email skipped (disabled): %s', subject)
        return False

    recipients = [email for email in to_emails if email]
    if not recipients:
        return False

    sender = settings_obj.default_from_email or settings_obj.smtp_username
    if not sender:
        return False

    try:
        connection = _mail_connection(settings_obj)
        email = EmailMessage(subject, body, sender, recipients, connection=connection)
        email.attach(f'{file_name}.pdf', uploaded_pdf_content.read(), 'application/pdf')
        email.send()
        return True
    except Exception:
        logger.exception('Failed to send attachment email: %s', subject)
        return False


def dispatch_marketing_mail(
    subject,
    body,
    recipient_list,
    *,
    files=None,
    company_profile=None,
    request=None,
) -> bool:
    company = company_profile or resolve_company_profile(request=request)
    if company is None:
        return False
    settings_obj = get_or_create_email_settings(company)
    if not settings_obj.enable_marketing_emails:
        logger.info('Marketing email skipped (disabled): %s', subject)
        return False

    recipients = [email.strip() for email in recipient_list if email and str(email).strip()]
    if not recipients:
        return False

    sender = (
        settings_obj.marketing_from_email
        or settings_obj.default_from_email
        or settings_obj.smtp_username
    )
    if not sender:
        logger.warning('Marketing email skipped (no from address configured): %s', subject)
        return False

    try:
        connection = _mail_connection(settings_obj)
        email = EmailMessage(subject, body, sender, recipients, connection=connection)
        for uploaded_file in files or []:
            content = uploaded_file.read()
            content_type = getattr(uploaded_file, 'content_type', None) or 'application/octet-stream'
            email.attach(uploaded_file.name, content, content_type)
        email.send()
        return True
    except Exception:
        logger.exception('Failed to send marketing email: %s', subject)
        return False


def send_test_email(settings_obj: CompanyEmailSettings, recipient: str) -> bool:
    connection = _mail_connection(settings_obj)
    sender = settings_obj.default_from_email or settings_obj.smtp_username
    if not sender or not recipient:
        return False
    send_mail(
        'Megawatt ERP — SMTP test',
        'This is a test email from your ERP email settings.',
        sender,
        [recipient],
        connection=connection,
        fail_silently=False,
    )
    return True
