"""SME-Hub template tags.

`smehub_status_badge` renders one canonical status pill for any SME-Hub status
vocabulary, so the status -> colour/label ladder lives in ONE place instead of
being copy-pasted across ~60 templates. Mirrors `rims_tags.rims_status_badge`.

Usage (always pass the raw status VALUE, not the object):

    {% load smehub_tags %}
    {% smehub_status_badge application.status 'incubation_application' %}
    {% smehub_status_badge biz.verification_status 'business_verification' %}
    {% smehub_status_badge listing.availability 'product_availability' %}

Each table maps a status value to (badge variant, canonical label). Labels were
canonicalised from the templates + model `choices` (verbose model labels such as
"Draft (auto-saved)" are trimmed to clean badge copy). Unknown values fall back
to a neutral badge with a prettified label, so a new enum member never crashes —
it just renders plainly until added here.

Badge variants in use: primary · neutral · green · amber · red · sky.
"""

import html as _html
import re

import bleach
from django import template
from django.utils.html import escape, format_html
from django.utils.html import linebreaks as _linebreaks
from django.utils.safestring import mark_safe

register = template.Library()


# ── Legacy rich-text rendering ────────────────────────────────────────────
# Migrated Drupal/Moodle records (programme objectives, call descriptions, …)
# carry raw HTML in plain TextFields. `|linebreaks` escapes that markup so the
# tags show as literal text; a bare `|safe` would render unsanitised HTML. Both
# filters below pass the value through bleach so the markup renders cleanly
# while scripts / unknown attributes are stripped.
#
#   {% load smehub_tags %}
#   <div class="prose">{{ programme.objectives|safe_html }}</div>   {# full render #}
#   <p class="line-clamp-3">{{ programme.objectives|strip_html }}</p> {# plain teaser #}

_ALLOWED_TAGS = {
    "p", "br", "hr", "div", "span",
    "a", "strong", "b", "em", "i", "u",
    "ul", "ol", "li",
    "h1", "h2", "h3", "h4", "h5", "h6",
    "blockquote", "pre", "code", "table", "thead", "tbody", "tr", "th", "td",
}
_ALLOWED_ATTRS = {"a": ["href", "title", "target", "rel"]}

# Cheap "does this look like markup?" probe — an opening/closing tag with a
# letter after `<`. Entities alone (&amp;) don't count; they render fine either way.
_TAG_RE = re.compile(r"</?[a-zA-Z][^>]*>")


@register.filter(is_safe=True)
def safe_html(value):
    """Sanitise legacy rich text and render it as HTML.

    Allows a conservative set of formatting tags (paragraphs, links, lists,
    headings, basic tables); strips scripts, styles and unknown attributes so
    imported markup is safe to render. Values with no markup at all (typed into
    a plain textarea) fall back to ``linebreaks``-style paragraphs so their
    newlines survive. Empty / falsey values yield ``""``.
    """
    if not value:
        return ""
    text = str(value)
    if not _TAG_RE.search(text):
        return mark_safe(_linebreaks(escape(text)))
    cleaned = bleach.clean(
        text,
        tags=_ALLOWED_TAGS,
        attributes=_ALLOWED_ATTRS,
        strip=True,
    )
    return mark_safe(cleaned)


@register.filter
def strip_html(value):
    """Reduce legacy rich text to a plain-text teaser.

    Strips every tag and unescapes entities (``&nbsp;`` → space) so list-card
    excerpts read as clean prose under ``line-clamp``. Django autoescapes the
    result, so it stays safe to render.
    """
    if not value:
        return ""
    text = bleach.clean(str(value), tags=set(), strip=True)
    return " ".join(_html.unescape(text).split())


@register.filter
def humanize_key(value):
    """Turn a snake_case identifier into a readable label.

    ``verified_business`` → ``Verified business``. Used for dict keys surfaced
    directly in templates (e.g. readiness threshold breakdowns) where the raw
    key would otherwise render with underscores.
    """
    if value is None:
        return ""
    text = str(value).replace("_", " ").strip()
    return text[:1].upper() + text[1:] if text else ""


# ── Applications ──────────────────────────────────────────────────────────
# Incubation Application.status — `rejected` reads "Not selected" (gentler,
# applicant-facing, matching the model's own label).
INCUBATION_APPLICATION = {
    "draft": ("amber", "Draft"),
    "submitted": ("primary", "Submitted"),
    "under_review": ("primary", "Under review"),
    "returned_for_info": ("amber", "Returned for info"),
    "accepted": ("green", "Accepted"),
    "rejected": ("red", "Not selected"),
    "withdrawn": ("neutral", "Withdrawn"),
}

# Investment FundingApplication.status — `rejected` reads "Rejected".
FUNDING_APPLICATION = {
    "draft": ("amber", "Draft"),
    "submitted": ("primary", "Submitted"),
    "under_review": ("primary", "Under review"),
    "returned_for_info": ("amber", "Returned for info"),
    "accepted": ("green", "Accepted"),
    "rejected": ("red", "Rejected"),
    "withdrawn": ("neutral", "Withdrawn"),
}

# ── Calls / programmes (draft · published · closed) ───────────────────────
CALL = {
    "draft": ("amber", "Draft"),
    "published": ("green", "Published"),
    "closed": ("neutral", "Closed"),
}

# Applicant-facing programme list relabels "published" -> "Open".
PROGRAMME_PUBLIC = {
    "draft": ("amber", "Draft"),
    "published": ("green", "Open"),
    "closed": ("neutral", "Closed"),
}

# ── Verification ladders ──────────────────────────────────────────────────
# Investor / BuyerRegistryEntry / linkage directory entries.
VERIFICATION = {
    "pending": ("amber", "Pending"),
    "verified": ("green", "Verified"),
    "rejected": ("red", "Rejected"),
    "deactivated": ("neutral", "Deactivated"),
}

# Onboarding Business.verification_status.
BUSINESS_VERIFICATION = {
    "pending_admin_review": ("neutral", "Pending review"),
    "verified": ("green", "Verified"),
    "rejected": ("red", "Rejected"),
    "revoked": ("red", "Revoked"),
}

# Onboarding EntrepreneurProfile.verification_status.
ENTREPRENEUR_VERIFICATION = {
    "pending_verification": ("neutral", "Pending"),
    "pending_more_info": ("amber", "More info requested"),
    "verified": ("green", "Verified"),
    "rejected": ("red", "Rejected"),
}

# ── Connections / pitch requests (pending · accepted · declined · withdrawn)
CONNECTION_REQUEST = {
    "pending": ("amber", "Pending"),
    "accepted": ("green", "Accepted"),
    "declined": ("red", "Declined"),
    "withdrawn": ("neutral", "Withdrawn"),
}

# ── Marketplace ───────────────────────────────────────────────────────────
DEMAND_RESPONSE = {
    "submitted": ("amber", "Submitted"),
    "shortlisted": ("primary", "Shortlisted"),
    "awarded": ("green", "Awarded"),
    "declined": ("red", "Declined"),
    "withdrawn": ("neutral", "Withdrawn"),
}

MARKET_DEMAND = {
    "open": ("green", "Open"),
    "closed": ("neutral", "Closed"),
    "awarded": ("sky", "Awarded"),
}

SALES_RECORD = {
    "pending_details": ("amber", "Pending details"),
    "recorded": ("primary", "Recorded"),
    "verified": ("green", "Verified"),
    "disputed": ("red", "Disputed"),
    "cancelled": ("neutral", "Cancelled"),
}

PRODUCT_AVAILABILITY = {
    "in_stock": ("green", "In stock"),
    "to_order": ("primary", "To order"),
    "seasonal": ("amber", "Seasonal"),
    "out_of_stock": ("neutral", "Out of stock"),
}

PRODUCT_STATUS = {
    "draft": ("amber", "Draft"),
    "published": ("green", "Published"),
    "unpublished": ("neutral", "Unpublished"),
}

# ── Onboarding innovation lifecycle ───────────────────────────────────────
INNOVATION_LIFECYCLE = {
    "draft": ("neutral", "Draft"),
    "submitted": ("primary", "Submitted"),
    "under_vetting": ("amber", "Under vetting"),
    "revisions_requested": ("amber", "Revisions requested"),
    "approved": ("green", "Approved"),
    "rejected": ("red", "Rejected"),
    "withdrawn": ("neutral", "Withdrawn"),
}

# ── Showcasing ────────────────────────────────────────────────────────────
SHOWCASING_APPLICATION = {
    "draft": ("neutral", "Draft"),
    "submitted": ("primary", "Submitted"),
    "under_review": ("primary", "Under review"),
    "confirmed": ("green", "Confirmed"),
    "rejected": ("amber", "Rejected"),
    "withdrawn": ("neutral", "Withdrawn"),
}

DEAL_ROOM = {
    "open": ("green", "Open"),
    "agreement_reached": ("sky", "Agreement reached"),
    "expired": ("neutral", "Expired"),
    "closed": ("neutral", "Closed"),
}

CHALLENGE = {
    "draft": ("amber", "Draft"),
    "open": ("green", "Open"),
    "judging": ("amber", "Judging"),
    "announced": ("green", "Winners announced"),
    "closed": ("neutral", "Closed"),
}

EVENT = {
    "draft": ("amber", "Draft"),
    "published": ("primary", "Published"),
    "live": ("green", "Live"),
    "concluded": ("neutral", "Concluded"),
}

# ── Linkage ───────────────────────────────────────────────────────────────
MOU = {
    "draft": ("neutral", "Draft"),
    "under_review": ("amber", "Under review"),
    "formalised": ("green", "Formalised"),
    "amended": ("sky", "Amended"),
    "terminated": ("red", "Terminated"),
}

SESSION = {
    "scheduled": ("amber", "Scheduled"),
    "completed": ("green", "Completed"),
    "cancelled": ("red", "Cancelled"),
    "no_show": ("neutral", "No show"),
}

RECOMMENDATION = {
    "submitted": ("amber", "Submitted"),
    "under_review": ("amber", "Under review"),
    "adopted": ("green", "Adopted"),
    "dismissed": ("red", "Dismissed"),
}

# ── Onboarding innovation evaluator assignment ────────────────────────────
EVALUATOR_ASSIGNMENT = {
    "assigned": ("primary", "Assigned"),
    "in_progress": ("amber", "In progress"),
    "completed": ("green", "Completed"),
    "recused": ("neutral", "Recused"),
}

# ── Onboarding meeting requests (imported `meeting_requests`) ─────────────
MEETING_REQUEST = {
    "request": ("amber", "Requested"),
    "accepted": ("green", "Accepted"),
    "rejected": ("red", "Rejected"),
    "cancelled": ("neutral", "Cancelled"),
}

# ── Incubation cohort membership ──────────────────────────────────────────
COHORT_MEMBER = {
    "active": ("primary", "Active"),
    "withdrawn": ("red", "Withdrawn"),
    "programme_complete": ("green", "Complete"),
}


_TABLES = {
    "incubation_application": INCUBATION_APPLICATION,
    "funding_application": FUNDING_APPLICATION,
    "funding_call": CALL,
    "programme": CALL,
    "programme_public": PROGRAMME_PUBLIC,
    "verification": VERIFICATION,
    "business_verification": BUSINESS_VERIFICATION,
    "entrepreneur_verification": ENTREPRENEUR_VERIFICATION,
    "connection_request": CONNECTION_REQUEST,
    "demand_response": DEMAND_RESPONSE,
    "market_demand": MARKET_DEMAND,
    "sales_record": SALES_RECORD,
    "product_availability": PRODUCT_AVAILABILITY,
    "product_status": PRODUCT_STATUS,
    "innovation_lifecycle": INNOVATION_LIFECYCLE,
    "showcasing_application": SHOWCASING_APPLICATION,
    "deal_room": DEAL_ROOM,
    "challenge": CHALLENGE,
    "event": EVENT,
    "mou": MOU,
    "session": SESSION,
    "advisory_session": SESSION,
    "mentor_session": SESSION,
    "recommendation": RECOMMENDATION,
    "evaluator_assignment": EVALUATOR_ASSIGNMENT,
    "meeting_request": MEETING_REQUEST,
    "cohort_member": COHORT_MEMBER,
}


@register.simple_tag
def smehub_status_badge(value, kind):
    """Render a consistent status badge for any SME-Hub status vocabulary.

    `value` is the raw status value (e.g. ``application.status``). Passing the
    model instance also works as long as it exposes ``.status``. Unknown values
    degrade to a neutral badge with a prettified label.
    """
    status = getattr(value, "status", value)
    table = _TABLES.get(kind, {})
    if status in table:
        variant, label = table[status]
    else:
        variant = "neutral"
        if hasattr(value, "get_status_display"):
            label = value.get_status_display()
        elif status:
            label = str(status).replace("_", " ").capitalize()
        else:
            label = "—"
    return format_html('<span class="badge badge-{}">{}</span>', variant, label)
