"""
Urgent alerts: site down, recovered, and anything else that cannot wait.

Sent to every system administrator on the install, by email, SMS and WhatsApp.
Each channel fails independently, so a missing Twilio key never stops the email
going out, and a broken mail server never stops the SMS.
"""
import logging

from django.conf import settings
from django.utils import timezone

logger = logging.getLogger(__name__)

# Roles that get woken up. Support agents and content managers do not.
ALERT_ROLES = ('system_admin', 'super_admin')


def _recipients():
    """Active system administrators, with whatever contact details they have."""
    from apps.accounts.models import User
    people = []
    for u in User.objects.filter(is_active=True, role__in=ALERT_ROLES):
        people.append({
            'name':  u.get_full_name() or u.username,
            'email': (u.email or '').strip(),
            'phone': (u.phone or '').strip(),
        })
    return people


def _panel_url(path=''):
    """
    Links must point at the panel subdomain, never at the public site with a
    /cms/ path. The panel host is where the session cookie lives, so a /cms/
    link on the public domain lands on a login screen, and it also exposes a
    URL that is meant to stay hidden.
    """
    from apps.tenants.models import Tenant
    host = ''
    try:
        master = Tenant.objects.filter(is_master=True).first()
        host = (getattr(master, 'cms_subdomain', '') or '').strip()
    except Exception:
        pass
    if not host:
        base = (getattr(settings, 'SITE_URL', '') or '').split('//')[-1].strip('/')
        host = f'panel.{base}' if base else ''
    if not host:
        return ''
    host = host.replace('https://', '').replace('http://', '').strip('/')
    return f"https://{host}/{path.lstrip('/')}"


def _site_label(tenant):
    """The domain identifies a site. One client can hold several."""
    return (getattr(tenant, 'primary_domain', '') or '').strip() or getattr(tenant, 'name', '') or 'Final Target'


def _humanise(delta):
    """3:40:22.145842 is not something to put in front of a person."""
    if delta is None:
        return ''
    seconds = int(delta.total_seconds())
    days, seconds = divmod(seconds, 86400)
    hours, seconds = divmod(seconds, 3600)
    minutes, seconds = divmod(seconds, 60)
    parts = []
    if days:
        parts.append(f'{days}d')
    if hours:
        parts.append(f'{hours}h')
    if minutes and not days:
        parts.append(f'{minutes}m')
    if not parts:
        parts.append(f'{seconds}s')
    return ' '.join(parts)


def send_incident_alert(incident, resolved=False):
    """
    Tell the administrators a site has gone down, or come back.
    Returns a small summary of what was actually delivered.
    """
    tenant = incident.tenant
    when   = timezone.localtime(incident.ended_at if resolved else incident.started_at)
    label  = _site_label(tenant)          # the domain
    name   = getattr(tenant, 'name', '')  # the client, shown quietly beside it

    if resolved:
        subject = f'Recovered: {label} is back online'
        headline = f'{label} is responding again'
    else:
        subject = f'URGENT: {label} is down'
        headline = f'{label} is not responding'

    context = {
        'resolved':      resolved,
        'headline':      headline,
        'site_name':     label,
        'client_name':   name,
        'cause':         incident.get_cause_display(),
        'status_code':   incident.status_code,
        'error':         incident.error,
        'diagnosis':     incident.diagnosis,
        'suggested_fix': incident.suggested_fix,
        'when':          when,
        'duration':      _humanise(incident.duration) if resolved else None,
        'fail_count':    incident.fail_count,
        'panel_url':     _panel_url(f'health/{tenant.pk}/'),
    }

    short = (
        f'{"RECOVERED" if resolved else "SITE DOWN"}: {label}. '
        f'{incident.get_cause_display()}. {when:%d %b %H:%M}. '
        + ('' if resolved else f'{incident.suggested_fix}')
    )[:300]

    sent = {'email': 0, 'sms': 0, 'whatsapp': 0}

    for person in _recipients():
        if person['email']:
            try:
                from apps.ecommerce.email_utils import _send
                _send(
                    subject=subject,
                    to=person['email'],
                    # Two separate templates rather than one with a colour
                    # switch, so each can be edited in the panel without any
                    # risk of changing the other.
                    template=('public/emails/incident_recovered.html' if resolved
                              else 'public/emails/incident_down.html'),
                    email_type='alert',
                    context=dict(context, recipient_name=person['name']),
                )
                sent['email'] += 1
            except Exception as exc:
                logger.warning('Alert email to %s failed: %s', person['email'], exc)

        if person['phone']:
            try:
                from apps.pages.sms_utils import send_sms
                if send_sms(person['phone'], short):
                    sent['sms'] += 1
            except Exception as exc:
                logger.warning('Alert SMS to %s failed: %s', person['phone'], exc)

            try:
                from apps.pages.whatsapp_utils import send_whatsapp
                if send_whatsapp(person['phone'], short):
                    sent['whatsapp'] += 1
            except Exception as exc:
                logger.warning('Alert WhatsApp to %s failed: %s', person['phone'], exc)

    logger.info('Incident alert for %s: %s', label, sent)
    return sent


def send_admin_alert(headline, intro, rows=None, panel_path='', short='',
                     kicker='Administrator Alert', accent='#b45309'):
    """
    Tell every system administrator that something irreversible happened.

    Same three channels as an incident alert, each failing independently, so a
    missing Twilio key never stops the email and a broken mail server never
    stops the SMS. Never raises: an alert that fails must not roll back the
    action it is reporting, or the record and the reality drift apart.

    rows: list of (label, value) pairs shown as a table in the email.
    """
    context = {
        'headline': headline,
        'intro':    intro,
        'rows':     rows or [],
        'kicker':   kicker,
        'accent':   accent,
        'panel_url': _panel_url(panel_path) if panel_path else '',
        'footnote': '',
    }
    subject = f'[Final Target] {headline}'
    short = (short or f'{headline}. {intro}')[:300]

    sent = {'email': 0, 'sms': 0, 'whatsapp': 0}
    try:
        people = _recipients()
    except Exception as exc:
        logger.warning('Admin alert could not list recipients: %s', exc)
        return sent

    for person in people:
        if person['email']:
            try:
                from apps.ecommerce.email_utils import _send
                _send(
                    subject=subject,
                    to=person['email'],
                    template='public/emails/admin_alert.html',
                    email_type='alert',
                    context=dict(context, recipient_name=person['name']),
                )
                sent['email'] += 1
            except Exception as exc:
                logger.warning('Admin alert email to %s failed: %s', person['email'], exc)

        if person['phone']:
            try:
                from apps.pages.sms_utils import send_sms
                if send_sms(person['phone'], short):
                    sent['sms'] += 1
            except Exception as exc:
                logger.warning('Admin alert SMS to %s failed: %s', person['phone'], exc)

            try:
                from apps.pages.whatsapp_utils import send_whatsapp
                if send_whatsapp(person['phone'], short):
                    sent['whatsapp'] += 1
            except Exception as exc:
                logger.warning('Admin alert WhatsApp to %s failed: %s', person['phone'], exc)

    logger.info('Admin alert "%s": %s', headline, sent)
    return sent
