from allianceauth.services.hooks import get_extension_logger
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.utils import timezone
from .app_settings import (
    OPCALENDAR_NOTIFY_IMPORTS,
    OPCALENDAR_NOTIFY_REPEAT_EVENTS,
    get_site_url,
)
from .models import Event, IngameEvents, _get
from .providers import esi

logger = get_extension_logger(__name__)

RED = 16711710
BLUE = 42751
GREEN = 6684416


def send_webhook(embed, hook, eve_time, content=None):
    """Helper function to send the webhook."""
    old = timezone.now() > eve_time
    if hook and hook.webhook and hook.webhook.enabled:
        if old and hook.ignore_past_fleets:
            logger.debug("Event is in the past, not sending webhook.")
        else:
            hook.webhook.send_embed(embed, content=content)


@receiver(post_save, sender=Event)
@receiver(post_save, sender=IngameEvents)
def fleet_saved(sender, instance, created, **kwargs):
    logger.debug("A new operation has been scheduled: %s", instance)
    col = GREEN if created else BLUE

    if sender == IngameEvents and OPCALENDAR_NOTIFY_IMPORTS:
        handle_ingame_event(instance, created, col)
    elif sender == Event:
        handle_standard_event(instance, created, col)


def handle_ingame_event(instance, created, col):
    """Handle IngameEvents notifications."""
    try:
        logger.debug("New signal created for Ingame Calendar Event: %s", instance.title)
        url = get_site_url() + f"/opcalendar/ingame/event/{instance.pk}/details/"
        message = f"New ingame calendar event: {instance.title}"
        category_map = {
            "alliance": "alliances",
            "corporation": "corporations",
            "character": "characters",
        }
        category = category_map.get(instance.owner_type, "characters")

        result = esi.client.Universe.PostUniverseIds(
            body=[instance.owner_name]
        ).result()
        matches = _get(result, category) or []
        if not matches:
            logger.debug(
                "No ESI match found for %s '%s', skipping notification",
                instance.owner_type,
                instance.owner_name,
            )
            return

        entity_id = _get(matches[0], "id")

        logger.debug("Entity data is %s", entity_id)
        portrait, ticker = get_entity_details(entity_id, instance.owner_type)

        embed = {
            "title": message,
            "description": instance.text,
            "url": url,
            "color": col,
            "fields": [
                {"name": "Owner", "value": instance.owner_name, "inline": True},
                {
                    "name": "Eve Time",
                    "value": instance.event_start_date.strftime("%Y-%m-%d %H:%M:%S"),
                },
            ],
            "footer": {"icon_url": portrait, "text": f"{instance.owner_name} {ticker}"},
        }
        send_webhook(embed, instance.owner.event_visibility, instance.event_start_date)
    except Exception as e:
        logger.exception(e)


def handle_standard_event(instance, created, col):
    """Handle standard Event notifications.

    The instant Discord ping only fires when "now" is one of the selected
    notification timings (and hasn't been sent yet). Other selected timings
    are picked up later by the send_scheduled_event_notifications task.
    """
    if not _should_send_now(instance):
        return

    if instance.repeat_event and not OPCALENDAR_NOTIFY_REPEAT_EVENTS:
        return

    handle_event_notification(instance, created, col, "New event:")
    _mark_notification_sent(instance, Event.NOTIFY_NOW)


def _should_send_now(instance):
    """Whether the immediate ('now') Discord ping should be sent for this event."""
    return (
        Event.NOTIFY_NOW in instance.notification_timings
        and Event.NOTIFY_NOW not in instance.notifications_sent
    )


def _mark_notification_sent(instance, timing):
    """Records a notification timing as sent without re-triggering save signals."""
    if timing in instance.notifications_sent:
        return
    instance.notifications_sent.append(timing)
    Event.objects.filter(pk=instance.pk).update(
        notifications_sent=instance.notifications_sent
    )


def handle_event_notification(instance, created, col, message_prefix, content=None):
    """Generate and send event notifications."""
    try:
        logger.debug("New signal fleet created for event %s", instance.title)
        url = get_site_url() + f"/opcalendar/event/{instance.pk}/details/"
        message = f"{message_prefix} {instance.title}"

        main_char = instance.eve_character
        if main_char:
            portrait = main_char.portrait_url_64
            character_name = main_char.character_name
            ticker = f"[{main_char.corporation_ticker}]"
        else:
            portrait = character_name = ticker = ""

        embed = {
            "title": message,
            "description": instance.description,
            "url": url,
            "color": col,
            "fields": [
                {"name": "FC", "value": instance.fc, "inline": True},
                {"name": "Type", "value": instance.operation_type.name, "inline": True},
                {"name": "Formup", "value": instance.formup_system, "inline": True},
                {
                    "name": "Eve Time",
                    "value": instance.start_time.strftime("%Y-%m-%d %H:%M:%S"),
                },
            ],
            "footer": {
                "icon_url": portrait,
                "text": f"{character_name} {ticker}, {instance.host}",
            },
        }
        send_webhook(
            embed, instance.event_visibility, instance.start_time, content=content
        )
    except Exception as e:
        logger.exception(e)


SCHEDULED_NOTIFICATION_PREFIXES = {
    Event.NOTIFY_1H_BEFORE: "Reminder: Fleet starting in 1 hour:",
    Event.NOTIFY_1D_BEFORE: "Reminder: Fleet starting in 1 day:",
    Event.NOTIFY_1W_BEFORE: "Reminder: Fleet starting in 1 week:",
    Event.NOTIFY_CUSTOM: "Reminder:",
}


def send_scheduled_notification(instance, timing):
    """Sends a scheduled Discord ping for the given notification timing and
    records it as sent. Called from send_scheduled_event_notifications."""
    message_prefix = SCHEDULED_NOTIFICATION_PREFIXES.get(timing, "Reminder:")
    handle_event_notification(instance, False, BLUE, message_prefix)
    _mark_notification_sent(instance, timing)


PING_TARGET_MENTIONS = {
    "here": "@here",
    "everyone": "@everyone",
}


def send_starting_now_ping(instance, ping_target):
    """Sends an immediate 'starting now' Discord ping with an @here/@everyone
    mention. Triggered manually by the event owner from the event details page."""
    mention = PING_TARGET_MENTIONS.get(ping_target)
    handle_event_notification(instance, False, GREEN, "Starting now:", content=mention)


def get_entity_details(entity_id, owner_type):
    """Retrieve the entity details based on the owner type."""
    if owner_type == "alliance":
        portrait = f"https://images.evetech.net/alliances/{entity_id}/logo"
        alliance = esi.client.Alliance.GetAlliancesAllianceId(
            alliance_id=entity_id
        ).result()
        ticker = f"[{_get(alliance, 'ticker')}]"
    elif owner_type == "corporation":
        portrait = f"https://images.evetech.net/corporations/{entity_id}/logo"
        corporation = esi.client.Corporation.GetCorporationsCorporationId(
            corporation_id=entity_id
        ).result()
        ticker = f"[{_get(corporation, 'ticker')}]"
    else:  # character
        portrait = f"https://images.evetech.net/characters/{entity_id}/portrait"
        ticker = ""
    return portrait, ticker


@receiver(pre_delete, sender=Event)
@receiver(pre_delete, sender=IngameEvents)
def fleet_deleted(sender, instance, **kwargs):
    logger.debug("An operation has been deleted: %s", instance)

    if sender == IngameEvents and OPCALENDAR_NOTIFY_IMPORTS:
        handle_event_deletion(instance, "Deleted Ingame Calendar Event:", RED)
    elif sender == Event:
        handle_event_deletion(instance, "Event deleted:", RED)


def handle_event_deletion(instance, message_prefix, color):
    """Handle event deletions and send notifications."""
    try:
        url = get_site_url() + f"/opcalendar/event/{instance.pk}/details/"
        message = f"{message_prefix} {instance.title}"

        main_char = instance.eve_character
        if main_char:
            portrait = main_char.portrait_url_64
            character_name = main_char.character_name
            ticker = f"[{main_char.corporation_ticker}]"
        else:
            portrait = character_name = ticker = ""

        embed = {
            "title": message,
            "description": instance.description,
            "url": url,
            "color": color,
            "fields": [
                {"name": "FC", "value": instance.fc, "inline": True},
                {"name": "Type", "value": instance.operation_type.name, "inline": True},
                {"name": "Formup", "value": instance.formup_system, "inline": True},
                {
                    "name": "Eve Time",
                    "value": instance.start_time.strftime("%Y-%m-%d %H:%M:%S"),
                },
            ],
            "footer": {
                "icon_url": portrait,
                "text": f"{character_name} {ticker}, {instance.host}",
            },
        }
        send_webhook(embed, instance.event_visibility, instance.start_time)
    except Exception as e:
        logger.exception(e)
