"""In-app notifications for everything a user needs to know about, per role.

Every function is safe to call from anywhere: a failure to notify never breaks the action that caused it.
"""
import logging

from django.urls import reverse

from .notification_service import create_user_notification

logger = logging.getLogger(__name__)

# Notification types grouped for the notification centre filters.
GROUPS = {
    'applications': ('application', 'job_application', 'application_status', 'interview'),
    'messages': ('message',),
    'jobs': ('job_alert', 'job_match', 'company_new_job', 'company_profile_updated', 'new_job'),
    'account': ('subscription', 'account', 'system', 'general', 'profile_view', 'testimonial', 'contact', 'payment', 'new_user', 'new_company'),
}
TYPE_ICONS = {
    'application': 'fa-paper-plane', 'job_application': 'fa-paper-plane', 'application_status': 'fa-clipboard-check',
    'interview': 'fa-calendar-check', 'message': 'fa-comments', 'job_alert': 'fa-bullseye', 'job_match': 'fa-bullseye',
    'company_new_job': 'fa-briefcase', 'company_profile_updated': 'fa-building', 'new_job': 'fa-briefcase',
    'subscription': 'fa-crown', 'payment': 'fa-credit-card', 'account': 'fa-user-check', 'testimonial': 'fa-star',
    'contact': 'fa-envelope', 'new_user': 'fa-user-plus', 'new_company': 'fa-building', 'profile_view': 'fa-eye',
    'system': 'fa-circle-info', 'general': 'fa-bell',
}


def _safe(fn):
    def wrapper(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except Exception:  # noqa: BLE001 - notifications must never break the caller
            logger.exception('Could not create notification in %s', fn.__name__)
            return None
    wrapper.__name__ = fn.__name__
    return wrapper


def _admins():
    from django.db.models import Q

    from .models import User

    return User.objects.filter(Q(role=User.Role.ADMIN) | Q(is_superuser=True), is_active=True).distinct()


def notify_admins(title, body, notification_type, path='', sender=None):
    for admin in _admins():
        create_user_notification(recipient=admin, title=title, body=body, notification_type=notification_type,
                                 path=path, sender=sender)


# ---------------------------------------------------------------- seekers
@_safe
def application_submitted(application):
    job = application.job
    create_user_notification(
        recipient=application.applicant, title='Application submitted',
        body=f'Your application for {job.title} at {job.company_name} was submitted. You can follow its progress from My Applications.',
        notification_type='application', path=reverse('seekers:applications'), button_text='View applications')


@_safe
def application_status_changed(application):
    job = application.job
    create_user_notification(
        recipient=application.applicant, title=f'Application update: {job.title}',
        body=f'Your application for {job.title} at {job.company_name} is now: {application.get_status_display()}.',
        notification_type='application_status', path=reverse('seekers:applications'), button_text='View applications')


@_safe
def interview_scheduled(interview):
    application = interview.application
    job = application.job
    when = interview.scheduled_at.strftime('%d %b %Y at %H:%M')
    place = interview.location or interview.meeting_url or 'details are on your applications page'
    create_user_notification(
        recipient=application.applicant, title=f'Interview scheduled: {job.title}',
        body=f'{job.company_name} scheduled an interview on {when}. Where: {place}.',
        notification_type='interview', path=reverse('seekers:applications'), button_text='View details')


@_safe
def job_alert(user, job, match_score):
    create_user_notification(
        recipient=user, title=f'New job match: {job.title}',
        body=f'{job.title} at {job.company_name} ({job.location}) matches your profile at {match_score}%.',
        notification_type='job_alert', path=job.get_absolute_url(), button_text='View job')


# ---------------------------------------------------------------- employers
@_safe
def new_application_received(application):
    job = application.job
    employer = job.posted_by
    if not employer:
        return
    name = application.applicant.get_full_name() or application.applicant.email
    create_user_notification(
        recipient=employer, sender=application.applicant, title=f'New application: {job.title}',
        body=f'{name} applied for {job.title}.',
        notification_type='application', path=reverse('ats:detail', args=[application.pk]), button_text='Review application')


# ---------------------------------------------------------------- everyone
@_safe
def new_message(message):
    conversation = message.conversation
    recipient = conversation.other_participant(message.sender)
    if not recipient:
        return
    sender_name = message.sender.get_full_name() or message.sender.email
    preview = message.body[:140] + ('...' if len(message.body) > 140 else '')
    create_user_notification(
        recipient=recipient, sender=message.sender, title=f'New message from {sender_name}', body=preview,
        notification_type='message', path=reverse('messaging:inbox_conversation', kwargs={'pk': conversation.pk}), button_text='Reply')


@_safe
def subscription_activated(user, plan, expires_at=None):
    until = f' It runs until {expires_at:%d %b %Y}.' if expires_at else ''
    create_user_notification(
        recipient=user, title='Subscription activated', body=f'Your {plan.name} plan is now active.{until}',
        notification_type='subscription', path=reverse('subscriptions:pricing'), button_text='View plans')


@_safe
def welcome(user):
    if user.is_company:
        body = 'Your employer account is ready. Complete your company profile, then post your first job.'
        path = reverse('employers:edit_company')
    else:
        body = 'Your account is ready. Complete your professional profile and upload your CV so employers can find you.'
        path = reverse('seekers:edit_profile')
    create_user_notification(recipient=user, title='Welcome to PEZAJOB', body=body, notification_type='account',
                             path=path, button_text='Complete profile')


# ---------------------------------------------------------------- admins
@_safe
def admin_new_user(user):
    kind = 'company' if user.is_company else 'job seeker'
    notify_admins('New account registered', f'{user.get_full_name() or user.email} joined as a {kind}.', 'new_user',
                  path=reverse('platform_admin:user_detail', args=[user.pk]) if _has_url('platform_admin:user_detail', user.pk) else '')


@_safe
def admin_new_company(company):
    notify_admins('New company registered', f'{company.name} created a company profile.', 'new_company')


@_safe
def admin_contact_message(message):
    notify_admins('New contact message', f'{message.full_name} sent a message about {message.get_subject_display()}.', 'contact',
                  path=reverse('platform_admin:messages') if _has_url('platform_admin:messages') else '')


@_safe
def admin_testimonial(testimonial):
    notify_admins('Testimonial awaiting review', f'{testimonial.full_name} submitted a testimonial.', 'testimonial',
                  path=reverse('platform_admin:testimonials') if _has_url('platform_admin:testimonials') else '')


@_safe
def admin_payment(payment):
    notify_admins('Subscription payment received', f'{payment.user.email} paid for {payment.plan.name} ({payment.get_status_display()}).', 'payment',
                  path=reverse('platform_admin:subscriptions') if _has_url('platform_admin:subscriptions') else '')


def _has_url(name, *args):
    try:
        reverse(name, args=args)
        return True
    except Exception:  # noqa: BLE001
        return False
