"""Cross-app signal receivers for SME-Hub investment (SP5).

  • Refreshes the SME performance snapshot and the investor-match list when
    upstream signals fire from SP1 (baseline), SP2 (incubation milestones),
    SP3 (linkage / MoU / advisory), SP4 (showcasing / deal rooms), and SP5
    itself (funding outcomes / readiness flips).
  • Re-evaluates investment readiness whenever the snapshot is refreshed.
"""
from __future__ import annotations

import logging

from django.dispatch import receiver

from apps.smehub.investment import signals as inv_signals
from apps.smehub.investment.services import (
    compile_performance_snapshot,
    compute_readiness,
    refresh_matches_for,
)

logger = logging.getLogger(__name__)


def _entrepreneur_for_user(user):
    if user is None:
        return None
    try:
        from apps.smehub.onboarding.models import EntrepreneurProfile
    except Exception:
        return None
    return EntrepreneurProfile.objects.filter(user=user).first()


def _refresh_pipeline(entrepreneur, *, trigger: str) -> None:
    """Snapshot → readiness → matches (in that order)."""
    if entrepreneur is None:
        return
    try:
        compile_performance_snapshot(entrepreneur, trigger=trigger)
        compute_readiness(entrepreneur)
        refresh_matches_for(entrepreneur, trigger=trigger)
    except Exception as exc:  # noqa: BLE001 — never block on receiver failure
        logger.warning("SP5 refresh pipeline failed for %s: %s", entrepreneur, exc)


# --- SP1 onboarding ------------------------------------------------------

def _wire_onboarding_receivers() -> None:
    try:
        from apps.smehub.onboarding import signals as onb_signals
    except Exception:
        return

    @receiver(onb_signals.smehub_baseline_initialised)
    def _on_baseline_initialised(sender, business=None, baseline=None, **kwargs):
        if business is None:
            return
        _refresh_pipeline(business.entrepreneur, trigger="baseline_initialised")


_wire_onboarding_receivers()


# --- SP2 incubation ------------------------------------------------------

def _wire_incubation_receivers() -> None:
    try:
        from apps.smehub.incubation import signals as inc_signals
    except Exception:
        return

    @receiver(inc_signals.smehub_application_decided)
    def _on_application_decided(sender, application=None, action=None, **kwargs):
        if application is None:
            return
        _refresh_pipeline(application.entrepreneur, trigger="incubation_application_decided")

    @receiver(inc_signals.smehub_cohort_confirmed)
    def _on_cohort_confirmed(sender, cohort=None, members=None, **kwargs):
        for member in members or []:
            _refresh_pipeline(member.entrepreneur, trigger="cohort_confirmed")

    @receiver(inc_signals.smehub_cohort_member_completed)
    def _on_member_completed(sender, member=None, **kwargs):
        if member is None:
            return
        _refresh_pipeline(member.entrepreneur, trigger="programme_completed")

    @receiver(inc_signals.smehub_certificate_issued)
    def _on_certificate_issued(sender, certificate=None, **kwargs):
        if certificate is None:
            return
        _refresh_pipeline(
            certificate.cohort_member.entrepreneur,
            trigger="certificate_issued",
        )


_wire_incubation_receivers()


# --- SP3 linkage ---------------------------------------------------------

def _wire_linkage_receivers() -> None:
    try:
        from apps.smehub.linkage import signals as lnk_signals
    except Exception:
        return

    @receiver(lnk_signals.smehub_connection_accepted)
    def _on_connection_accepted(sender, connection=None, connection_request=None, **kwargs):
        target = connection or connection_request
        if target is None:
            return
        _refresh_pipeline(target.entrepreneur, trigger="connection_accepted")

    @receiver(lnk_signals.smehub_mou_formalised)
    def _on_mou_formalised(sender, mou=None, **kwargs):
        if mou is None:
            return
        _refresh_pipeline(mou.connection.entrepreneur, trigger="mou_formalised")

    @receiver(lnk_signals.smehub_advisory_completed)
    def _on_advisory_completed(sender, session=None, **kwargs):
        if session is None:
            return
        _refresh_pipeline(session.entrepreneur, trigger="advisory_completed")


_wire_linkage_receivers()


# --- SP4 showcasing ------------------------------------------------------

def _wire_showcasing_receivers() -> None:
    try:
        from apps.smehub.showcasing import signals as show_signals
    except Exception:
        return

    @receiver(show_signals.smehub_showcase_participant_confirmed)
    def _on_participant_confirmed(sender, application=None, **kwargs):
        if application is None:
            return
        _refresh_pipeline(application.entrepreneur, trigger="showcase_confirmed")

    @receiver(show_signals.smehub_catalogue_published)
    def _on_catalogue_published(sender, entry=None, **kwargs):
        if entry is None:
            return
        _refresh_pipeline(entry.entrepreneur, trigger="catalogue_published")

    @receiver(show_signals.smehub_agreement_formalised)
    def _on_agreement_formalised(sender, deal_room=None, **kwargs):
        if deal_room is None:
            return
        _refresh_pipeline(deal_room.entrepreneur, trigger="agreement_formalised")


_wire_showcasing_receivers()


# --- SP5 internal --------------------------------------------------------

@receiver(inv_signals.smehub_funding_application_decided)
def _on_funding_application_decided(sender, application=None, **kwargs):
    if application is None:
        return
    _refresh_pipeline(application.entrepreneur, trigger="funding_application_decided")


@receiver(inv_signals.smehub_funding_secured)
def _on_funding_secured(sender, entrepreneur=None, **kwargs):
    if entrepreneur is None:
        return
    _refresh_pipeline(entrepreneur, trigger="funding_secured")


@receiver(inv_signals.smehub_manual_funding_recorded)
def _on_manual_funding_recorded(sender, record=None, **kwargs):
    if record is None:
        return
    _refresh_pipeline(record.entrepreneur, trigger="manual_funding_recorded")
