"""SME-Hub onboarding views (SP1).

Each view either renders a full page (extends `layouts/dashboard.html` or
`layouts/auth.html` / `layouts/public.html`) or returns a partial template
when the request originates from HTMX.  Role gating uses the mixins in
`apps.smehub._shared.role_gates`.
"""
from __future__ import annotations

from django.contrib import messages
from django.contrib.auth import get_user_model, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.dateparse import parse_date
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.http import require_GET, require_POST, require_http_methods
from django.views.generic import ListView, TemplateView, UpdateView

from apps.core.authentication.models import Institution
from apps.smehub._shared.repository import relevant_repository_documents
from apps.smehub._shared.role_gates import (
    SME_ADMIN_ROLES,
    EntrepreneurRequired,
    EntrepreneurVerifiedRequired,
    EvaluatorRequired,
    MentorRequired,
    SMEAdminRequired,
)
from apps.smehub.investment.models import Investor
from apps.smehub.linkage.models import PartnerDirectoryEntry, ServiceProviderEntry
from apps.smehub.marketplace.models import BuyerRegistryEntry
from apps.smehub.onboarding import services
from apps.smehub.onboarding.forms import (
    AIHAffiliationForm,
    BackendRegisterForm,
    BulkVerifyForm,
    BusinessRegistrationForm,
    BuyerProfileForm,
    EntrepreneurProfileForm,
    EvaluatorAssignmentForm,
    InnovationDraftForm,
    InvestorProfileForm,
    MeetingRequestForm,
    MeetingRespondForm,
    MentorProfileForm,
    MoreInfoMessageForm,
    PartnerProfileForm,
    RegistrationForm,
    RejectionReasonForm,
    ResendVerificationForm,
    ServiceProviderProfileForm,
    VettingDecisionForm,
    VettingRecommendationForm,
    VettingScoreForm,
)
from apps.smehub.onboarding.models import (
    AIHAffiliation,
    Business,
    BuyerProfile,
    EntrepreneurProfile,
    EvaluatorAssignment,
    Faq,
    Innovation,
    InvestorProfile,
    MeetingRequest,
    MentorProfile,
    MentorshipCategory,
    NewsletterSubscriber,
    PartnerProfile,
    RegistrationRecord,
    ServiceProviderProfile,
    VettingRecord,
    VettingScore,
)


# ---------------------------------------------------------------------------
# Public registration  (FRSME-OBR001 → OBR010)
# ---------------------------------------------------------------------------

class RegisterView(View):
    template_name = "smehub/onboarding/register.html"

    def get(self, request):
        form = RegistrationForm()
        return render(request, self.template_name, {"form": form})

    def post(self, request):
        form = RegistrationForm(request.POST)
        if not form.is_valid():
            return render(request, self.template_name, {"form": form}, status=400)
        try:
            services.register_user(
                full_name=form.cleaned_data["full_name"],
                email=form.cleaned_data["email"],
                password=form.cleaned_data["password"],
                country=form.cleaned_data["country"],
                organisation=form.cleaned_data["organisation"],
                phone=form.cleaned_data["phone"],
                role=form.cleaned_data["role"],
            )
        except services.DuplicateEmailError:
            form.add_error("email", "Email already in use.")
            return render(request, self.template_name, {"form": form}, status=400)
        return redirect("smehub_onboarding:verify_email_sent")


@require_GET
def verify_email_sent(request):
    return render(request, "smehub/onboarding/verify_email_sent.html")


@require_http_methods(["GET", "POST"])
def resend_verification(request):
    if request.method == "POST":
        form = ResendVerificationForm(request.POST)
        if form.is_valid():
            services.resend_verification(email=form.cleaned_data["email"])
            messages.success(
                request,
                "If a pending registration exists for that email, a new link is on its way.",
            )
            return redirect("smehub_onboarding:verify_email_sent")
    else:
        form = ResendVerificationForm()
    return render(request, "smehub/onboarding/verify_email_resend.html", {"form": form})


@require_GET
def verify_email(request, token: str):
    try:
        user = services.verify_email(token=token)
    except services.TokenInvalidError as exc:
        return render(
            request,
            "smehub/onboarding/verify_email_expired.html",
            {"reason": str(exc)},
            status=410,
        )
    user.backend = "django.contrib.auth.backends.ModelBackend"
    login(request, user)
    if user.role == "entrepreneur":
        return redirect("smehub_onboarding:entrepreneur_profile_wizard")
    return redirect("smehub_onboarding:role_profile_complete")


# ---------------------------------------------------------------------------
# Entrepreneur profile wizard  (FRSME-OBR011 → OBR014)
# ---------------------------------------------------------------------------

class EntrepreneurProfileWizardView(EntrepreneurRequired, View):
    template_name = "smehub/onboarding/entrepreneur_profile_wizard.html"

    def get(self, request):
        profile, _ = EntrepreneurProfile.objects.get_or_create(user=request.user)
        # FRSME-OBR002 — registration already captured country / organisation /
        # phone; prefill any still-blank profile field from the latest
        # RegistrationRecord so the entrepreneur never re-types them.
        initial = {}
        if not (profile.country and profile.organisation and profile.phone):
            record = (
                RegistrationRecord.objects.filter(email__iexact=request.user.email)
                .order_by("-created_at")
                .first()
            )
            if record:
                if not profile.country and record.country:
                    initial["country"] = record.country
                if not profile.organisation and record.organisation:
                    initial["organisation"] = record.organisation
                if not profile.phone and record.phone:
                    initial["phone"] = record.phone
        form = EntrepreneurProfileForm(instance=profile, initial=initial or None)
        rims_match = services.lookup_rims_match(request.user.email)
        ctx = {
            "profile": profile,
            "form": form,
            "rims_match": rims_match,
        }
        return render(request, self.template_name, ctx)

    def post(self, request):
        profile, _ = EntrepreneurProfile.objects.get_or_create(user=request.user)
        form = EntrepreneurProfileForm(request.POST, request.FILES, instance=profile)
        if not form.is_valid():
            rims_match = services.lookup_rims_match(request.user.email)
            return render(
                request,
                self.template_name,
                {"profile": profile, "form": form, "rims_match": rims_match},
                status=400,
            )
        services.submit_entrepreneur_profile(
            user=request.user,
            country=form.cleaned_data["country"],
            organisation=form.cleaned_data["organisation"],
            phone=form.cleaned_data["phone"],
            bio=form.cleaned_data["bio"],
            rims_match_application_id=form.cleaned_data.get("rims_match_application_id"),
            rims_match_scholar_id=form.cleaned_data.get("rims_match_scholar_id"),
        )
        # Photo handled by the ModelForm save() side-effect on `profile`
        if form.cleaned_data.get("photo"):
            profile.photo = form.cleaned_data["photo"]
            profile.save(update_fields=["photo", "updated_at"])
        messages.success(request, "Profile submitted. You'll be notified once verified.")
        return redirect("smehub_onboarding:my_dashboard")


# ---------------------------------------------------------------------------
# Generic role-profile completion  (mentor / investor / service / partner / buyer)
# ---------------------------------------------------------------------------

ROLE_FORM_MAP = {
    "mentor": (
        MentorProfile, MentorProfileForm, "Mentor",
        "Tell us your areas of expertise so we can match you with entrepreneurs who need your guidance.",
    ),
    "investor": (
        InvestorProfile, InvestorProfileForm, "Investor",
        "Tell us about your investment focus so we can surface relevant ventures.",
    ),
    "service_provider": (
        ServiceProviderProfile, ServiceProviderProfileForm, "Service provider",
        "List the services you offer so entrepreneurs can book advisory sessions with you.",
    ),
    "partner": (
        PartnerProfile, PartnerProfileForm, "Partner organisation",
        "Describe your organisation so entrepreneurs and SME-Hub admins can connect with you for collaborations and MoUs.",
    ),
    "buyer": (
        BuyerProfile, BuyerProfileForm, "Buyer / off-taker",
        "Describe what your organisation procures so entrepreneurs can match against your demand listings.",
    ),
}

# Primary browse destination per non-entrepreneur role — gives a migrated
# investor/mentor/etc. somewhere to go from My Dashboard before they hold a
# heavier directory entry (Investor / ServiceProviderEntry / …).
ROLE_HUB = {
    "investor": ("smehub_investment:hub", "Investment hub",
                 "Browse ventures raising capital and open funding calls."),
    "mentor": ("smehub_incubation:hub", "Incubation hub",
               "See programmes and the entrepreneurs you can support."),
    "service_provider": ("smehub_linkage:hub", "Linkage & network hub",
                         "Offer advisory services and respond to engagement requests."),
    "partner": ("smehub_linkage:hub", "Linkage & network hub",
                "Connect with ventures and manage MoUs."),
    "buyer": ("smehub_marketplace:hub", "Marketplace hub",
              "Browse products and post your demand listings."),
}


class RoleProfileCompletionView(LoginRequiredMixin, View):
    """Single endpoint for non-entrepreneur roles to complete their profile."""

    template_name = "smehub/onboarding/role_profile_form.html"

    def get(self, request):
        role = request.user.role or ""
        if role == "entrepreneur":
            return redirect("smehub_onboarding:entrepreneur_profile_wizard")
        if role not in ROLE_FORM_MAP:
            messages.error(request, "Your role does not have an SME-Hub profile.")
            return redirect("/")
        model, form_cls, label, blurb = ROLE_FORM_MAP[role]
        instance, _ = model.objects.get_or_create(user=request.user)
        return render(
            request,
            self.template_name,
            {
                "form": form_cls(instance=instance),
                "instance": instance,
                "role_label": label,
                "role_blurb": blurb,
            },
        )

    def post(self, request):
        role = request.user.role or ""
        if role not in ROLE_FORM_MAP:
            return redirect("/")
        model, form_cls, label, blurb = ROLE_FORM_MAP[role]
        instance, _ = model.objects.get_or_create(user=request.user)
        form = form_cls(request.POST, instance=instance)
        if not form.is_valid():
            return render(
                request,
                self.template_name,
                {
                    "form": form,
                    "instance": instance,
                    "role_label": label,
                    "role_blurb": blurb,
                },
                status=400,
            )
        # JSON-listed fields are not direct ModelForm fields — apply manually.
        for field in ("expertise_sectors", "languages", "services_offered", "sectors_sourced"):
            if field in form.cleaned_data:
                setattr(instance, field, form.cleaned_data[field])
        form.save()
        instance.save()
        messages.success(request, "Profile saved. An administrator will verify your account shortly.")
        return redirect("smehub_onboarding:my_dashboard")


# ---------------------------------------------------------------------------
# Business registration & affiliation  (FRSME-OBR021 → OBR036)
# ---------------------------------------------------------------------------

_BUSINESS_REGISTER_WIZARD_STEPS = [
    {"n": 1, "title": "Identity"},
    {"n": 2, "title": "Location & Story"},
    {"n": 3, "title": "M&E baseline"},
]


class BusinessRegisterView(EntrepreneurVerifiedRequired, View):
    template_name = "smehub/onboarding/business_register.html"

    def _ctx(self, form):
        return {
            "form": form,
            "wizard_steps": _BUSINESS_REGISTER_WIZARD_STEPS,
            "cancel_url": reverse("smehub_onboarding:my_dashboard"),
        }

    def get(self, request):
        return render(request, self.template_name, self._ctx(BusinessRegistrationForm()))

    def post(self, request):
        form = BusinessRegistrationForm(request.POST, request.FILES)
        if not form.is_valid():
            return render(request, self.template_name, self._ctx(form), status=400)
        try:
            business = services.register_business(
                entrepreneur=request.user.smehub_entrepreneur,
                name=form.cleaned_data["name"],
                sector=form.cleaned_data["sector"],
                sub_sector=form.cleaned_data["sub_sector"],
                business_stage=form.cleaned_data["business_stage"],
                founding_date=form.cleaned_data["founding_date"],
                country=form.cleaned_data["country"],
                location=form.cleaned_data["location"],
                description=form.cleaned_data["description"],
                target_market=form.cleaned_data["target_market"],
                fte_count=form.cleaned_data["fte_count"],
                pte_count=form.cleaned_data["pte_count"],
                revenue_range=form.cleaned_data["revenue_range"],
                primary_target_market=form.cleaned_data["primary_target_market"],
                geographic_reach=form.cleaned_data["geographic_reach"],
            )
        except services.EntrepreneurNotVerifiedError:
            messages.error(request, "Your entrepreneur profile must be verified first.")
            return redirect("smehub_onboarding:my_dashboard")
        if form.cleaned_data.get("logo"):
            business.logo = form.cleaned_data["logo"]
            business.save(update_fields=["logo", "updated_at"])
        messages.success(request, f"Business registered with ID {business.business_id}. Awaiting admin review.")
        return redirect("smehub_onboarding:my_businesses")


class MyBusinessesView(EntrepreneurRequired, ListView):
    model = Business
    template_name = "smehub/onboarding/my_businesses.html"
    context_object_name = "businesses"
    paginate_by = 20

    def get_queryset(self):
        return (
            Business.objects.filter(entrepreneur__user=self.request.user)
            .order_by("-created_at")
        )


class BusinessDetailView(EntrepreneurRequired, View):
    template_name = "smehub/onboarding/business_detail.html"

    def get(self, request, business_id: int):
        business = get_object_or_404(
            Business,
            pk=business_id,
            entrepreneur__user=request.user,
        )
        return render(
            request,
            self.template_name,
            {
                "business": business,
                **_business_activity_context(business),
            },
        )


def _business_activity_context(business: Business) -> dict:
    """Sales + inbound meeting requests shown on both business detail pages."""
    from django.contrib.contenttypes.models import ContentType

    sales = business.sales.exclude(status="cancelled").order_by("-sale_date", "-recorded_at")
    meeting_requests = (
        MeetingRequest.objects.filter(
            recipient_content_type=ContentType.objects.get_for_model(Business),
            recipient_object_id=business.pk,
        )
        .select_related("requester")
        .order_by("-meeting_time")
    )
    return {
        "recent_sales": sales[:5],
        "sales_total": sales.count(),
        "meeting_requests": meeting_requests,
    }


class AIHAffiliationView(EntrepreneurRequired, View):
    template_name = "smehub/onboarding/aih_affiliation_form.html"

    def get(self, request, business_id: int):
        business = get_object_or_404(
            Business,
            pk=business_id,
            entrepreneur__user=request.user,
            verification_status=Business.Status.VERIFIED,
        )
        return render(
            request,
            self.template_name,
            {
                "business": business,
                "form": AIHAffiliationForm(),
                "affiliations": business.affiliations.select_related("institution"),
                "institutions": Institution.objects.filter(is_active=True).order_by("name"),
            },
        )

    def post(self, request, business_id: int):
        business = get_object_or_404(
            Business,
            pk=business_id,
            entrepreneur__user=request.user,
            verification_status=Business.Status.VERIFIED,
        )
        form = AIHAffiliationForm(request.POST)
        if not form.is_valid():
            return render(
                request,
                self.template_name,
                {
                    "business": business,
                    "form": form,
                    "affiliations": business.affiliations.select_related("institution"),
                    "institutions": Institution.objects.filter(is_active=True).order_by("name"),
                },
                status=400,
            )
        try:
            services.record_aih_affiliation(
                business=business,
                institution_id=form.cleaned_data["institution"].pk,
                nature=form.cleaned_data["nature"],
                programme_of_origin=form.cleaned_data["programme_of_origin"],
                is_primary=form.cleaned_data["is_primary"],
                rims_award_id=form.cleaned_data.get("rims_award_id"),
                rims_scholar_id=form.cleaned_data.get("rims_scholar_id"),
            )
        except services.AmbiguousAwardMatchError as exc:
            # FRSME-OBR alt-flow A7: present the candidate awards for the
            # entrepreneur to pick from, instead of 500'ing.
            return render(
                request,
                self.template_name,
                {
                    "business": business,
                    "form": form,
                    "affiliations": business.affiliations.select_related("institution"),
                    "institutions": Institution.objects.filter(is_active=True).order_by("name"),
                    "ambiguous_awards": exc.candidates,
                    "ambiguous_message": (
                        "More than one RIMS award matches this programme of origin. "
                        "Pick the correct one, or contact an administrator if none apply."
                    ),
                },
                status=409,
            )
        messages.success(request, "Affiliation recorded.")
        return redirect("smehub_onboarding:business_aih", business_id=business.pk)


# ---------------------------------------------------------------------------
# Entrepreneur dashboard
# ---------------------------------------------------------------------------

class MyDashboardView(LoginRequiredMixin, TemplateView):
    template_name = "smehub/onboarding/my_dashboard.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        user = self.request.user
        ctx["entrepreneur"] = getattr(user, "smehub_entrepreneur", None)
        businesses = (
            list(Business.objects.filter(entrepreneur__user=user).order_by("-created_at"))
            if ctx["entrepreneur"]
            else []
        )
        ctx["businesses"] = businesses

        # Slice B.2 cont. — role-aware workspace cards. Each card only renders
        # when the logged-in user holds that role profile, so a single
        # "My SME-Hub" landing covers entrepreneurs, mentors, buyers,
        # service providers, partners, and investors without a separate
        # "My Workspace" nav item.
        role = getattr(user, "role", None)

        # Admins and platform staff never hold an SME-Hub profile — they review
        # them. Give that audience an explanation plus their real workspaces
        # (verification / pipeline / operations hubs) instead of the empty
        # "Signed in as …" shell.
        ctx["is_sme_admin"] = user.is_superuser or role in SME_ADMIN_ROLES
        if ctx["is_sme_admin"]:
            ctx["pending_entrepreneurs"] = EntrepreneurProfile.objects.filter(
                verification_status__in=[
                    EntrepreneurProfile.Status.PENDING_VERIFICATION,
                    EntrepreneurProfile.Status.PENDING_MORE_INFO,
                ],
            ).count()
            ctx["pending_businesses"] = Business.objects.filter(
                verification_status=Business.Status.PENDING_ADMIN_REVIEW,
            ).count()

        # Role-aware profile panel for non-entrepreneur personas (investor,
        # mentor, service provider, partner, buyer). Migrated investors now hold
        # a real InvestorProfile shell, so the dashboard must reflect their
        # verification status and point them at the right hub — not the
        # entrepreneur "register a business" flow.
        ctx["role_profile"] = None
        ctx["role_label"] = ""
        ctx["role_hub"] = None
        ctx["role_completable"] = bool(not ctx["entrepreneur"] and role in ROLE_FORM_MAP)
        if not ctx["entrepreneur"] and role in ROLE_FORM_MAP:
            profile_model, _, role_label, _ = ROLE_FORM_MAP[role]
            ctx["role_profile"] = profile_model.objects.filter(user=user).first()
            ctx["role_label"] = role_label
            if role in ROLE_HUB:
                hub_url, hub_name, hub_blurb = ROLE_HUB[role]
                ctx["role_hub"] = {"url_name": hub_url, "name": hub_name, "blurb": hub_blurb}

        ctx["workspaces"] = workspaces = []
        if role == "mentor":
            workspaces.append({
                "name": "Mentor dashboard",
                "url_name": "smehub_incubation:mentor_dashboard",
                "blurb": "Review mentees, log sessions, record readiness assessments.",
            })
        if BuyerRegistryEntry.objects.filter(user=user).exists():
            workspaces.append({
                "name": "Buyer workspace",
                "url_name": "smehub_marketplace:buyer_dashboard",
                "blurb": "Manage demand listings, triage responses, post awards.",
            })
        if ServiceProviderEntry.objects.filter(managed_by=user).exists():
            workspaces.append({
                "name": "Service-provider workspace",
                "url_name": "smehub_linkage:service_provider_dashboard",
                "blurb": "Track active engagements and advisory bookings.",
            })
        if PartnerDirectoryEntry.objects.filter(managed_by=user).exists():
            workspaces.append({
                "name": "Partner workspace",
                "url_name": "smehub_linkage:partner_dashboard",
                "blurb": "Connection requests, MoUs, profile management.",
            })
        if Investor.objects.filter(contact_user=user).exists():
            workspaces.append({
                "name": "Investor workspace",
                "url_name": "smehub_investment:investor_dashboard",
                "blurb": "Pipeline, deal rooms, pitch traffic.",
            })

        # Repository picks — top documents tagged with the user's verified-business sectors.
        picks: list = []
        seen_ids: set = set()
        for biz in businesses:
            if biz.verification_status != Business.Status.VERIFIED:
                continue
            for doc in relevant_repository_documents(biz, limit=6):
                if doc.pk in seen_ids:
                    continue
                seen_ids.add(doc.pk)
                picks.append(doc)
                if len(picks) >= 6:
                    break
            if len(picks) >= 6:
                break
        ctx["repository_picks"] = picks
        return ctx


# ---------------------------------------------------------------------------
# Admin queues  (FRSME-OBR015 → OBR020 / OBR026 → OBR029)
# ---------------------------------------------------------------------------

def _filter_by_submission_date(qs, request, field="submitted_at"):
    """FRSME-OBR015 / OBR026 — narrow a verification queue by submission date.

    Reads ``date_from`` / ``date_to`` GET params (YYYY-MM-DD) and applies an
    inclusive range on ``field`` (defaults to ``submitted_at``). Invalid or
    absent values are ignored, so an empty filter bar returns the full queue.
    """
    date_from = parse_date((request.GET.get("date_from") or "").strip())
    date_to = parse_date((request.GET.get("date_to") or "").strip())
    if date_from:
        qs = qs.filter(**{f"{field}__date__gte": date_from})
    if date_to:
        qs = qs.filter(**{f"{field}__date__lte": date_to})
    return qs


class EntrepreneurQueueView(SMEAdminRequired, ListView):
    model = EntrepreneurProfile
    template_name = "smehub/onboarding/entrepreneur_queue.html"
    context_object_name = "profiles"
    paginate_by = 20

    def get_queryset(self):
        qs = EntrepreneurProfile.objects.select_related("user").order_by("-submitted_at", "-created_at")
        status = self.request.GET.get("status") or "pending"
        if status == "pending":
            qs = qs.filter(verification_status__in=[
                EntrepreneurProfile.Status.PENDING_VERIFICATION,
                EntrepreneurProfile.Status.PENDING_MORE_INFO,
            ])
        elif status == "verified":
            qs = qs.filter(verification_status=EntrepreneurProfile.Status.VERIFIED)
        elif status == "rejected":
            qs = qs.filter(verification_status=EntrepreneurProfile.Status.REJECTED)
        # Slice C — "all" was in the UI but silently fell through, leaving the
        # default pending filter applied. Now genuinely returns the full set.
        elif status == "all":
            pass
        country = self.request.GET.get("country")
        if country:
            qs = qs.filter(country__iexact=country)
        institution = self.request.GET.get("institution")
        if institution:
            qs = qs.filter(organisation__icontains=institution)
        qs = _filter_by_submission_date(qs, self.request)
        return qs

    def get_template_names(self):
        if self.request.htmx:
            return ["smehub/onboarding/partials/entrepreneur_queue_results.html"]
        return [self.template_name]


class EntrepreneurDetailView(SMEAdminRequired, View):
    template_name = "smehub/onboarding/entrepreneur_detail_admin.html"

    def get(self, request, pk: int):
        profile = get_object_or_404(EntrepreneurProfile.objects.select_related("user"), pk=pk)
        return render(
            request,
            self.template_name,
            {
                "profile": profile,
                "rejection_form": RejectionReasonForm(),
                "info_form": MoreInfoMessageForm(),
            },
        )


@require_POST
@login_required
def verify_entrepreneur_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    profile = get_object_or_404(EntrepreneurProfile, pk=pk)
    services.verify_entrepreneur(profile, by_user=request.user)
    if request.htmx:
        return render(request, "smehub/onboarding/partials/entrepreneur_queue_row.html", {"profile": profile})
    messages.success(request, f"{profile.user.get_full_name()} verified.")
    return redirect("smehub_onboarding:entrepreneur_detail", pk=pk)


@require_POST
@login_required
def reject_entrepreneur_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    profile = get_object_or_404(EntrepreneurProfile, pk=pk)
    form = RejectionReasonForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Rejection reason required.")
        return redirect("smehub_onboarding:entrepreneur_detail", pk=pk)
    services.reject_entrepreneur(profile, by_user=request.user, reason=form.cleaned_data["reason"])
    messages.success(request, f"{profile.user.get_full_name()} rejected.")
    return redirect("smehub_onboarding:entrepreneur_detail", pk=pk)


@require_POST
@login_required
def request_more_info_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    profile = get_object_or_404(EntrepreneurProfile, pk=pk)
    form = MoreInfoMessageForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Message required.")
        return redirect("smehub_onboarding:entrepreneur_detail", pk=pk)
    services.request_more_info_from_entrepreneur(
        profile, by_user=request.user, message=form.cleaned_data["message"],
    )
    messages.success(request, "Information request sent.")
    return redirect("smehub_onboarding:entrepreneur_detail", pk=pk)


@require_POST
@login_required
def bulk_verify_view(request):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    form = BulkVerifyForm(request.POST)
    if not form.is_valid():
        messages.error(request, "No entrepreneurs selected.")
        return redirect("smehub_onboarding:entrepreneur_queue")
    count = services.bulk_verify_entrepreneurs(form.cleaned_data["ids"], by_user=request.user)
    messages.success(request, f"{count} entrepreneurs verified.")
    return redirect("smehub_onboarding:entrepreneur_queue")


# ---------------------------------------------------------------------------
# Business admin queue
# ---------------------------------------------------------------------------

class BusinessQueueView(SMEAdminRequired, ListView):
    model = Business
    template_name = "smehub/onboarding/business_queue.html"
    context_object_name = "businesses"
    paginate_by = 20

    def get_queryset(self):
        qs = Business.objects.select_related("entrepreneur__user").order_by("-created_at")
        status = self.request.GET.get("status") or "pending"
        if status == "pending":
            qs = qs.filter(verification_status=Business.Status.PENDING_ADMIN_REVIEW)
        elif status == "verified":
            qs = qs.filter(verification_status=Business.Status.VERIFIED)
        elif status == "rejected":
            qs = qs.filter(verification_status=Business.Status.REJECTED)
        elif status == "revoked":
            qs = qs.filter(verification_status=Business.Status.REVOKED)
        elif status == "all":
            pass  # Slice C — honour the "all" filter that was already in the UI.
        sector = self.request.GET.get("sector")
        if sector:
            qs = qs.filter(sector__icontains=sector)
        country = self.request.GET.get("country")
        if country:
            qs = qs.filter(country__iexact=country)
        stage = self.request.GET.get("stage")
        if stage:
            qs = qs.filter(business_stage=stage)
        # Business has no separate submitted_at — registration time (created_at)
        # is the submission date the SRS refers to.
        qs = _filter_by_submission_date(qs, self.request, field="created_at")
        return qs

    def get_template_names(self):
        if self.request.htmx:
            return ["smehub/onboarding/partials/business_queue_results.html"]
        return [self.template_name]


class BusinessDetailAdminView(SMEAdminRequired, View):
    template_name = "smehub/onboarding/business_detail_admin.html"

    def get(self, request, pk: int):
        business = get_object_or_404(
            Business.objects.select_related("entrepreneur__user", "baseline").prefetch_related("affiliations__institution"),
            pk=pk,
        )
        return render(
            request,
            self.template_name,
            {
                "business": business,
                "rejection_form": RejectionReasonForm(),
                "revoke_form": RejectionReasonForm(),
                **_business_activity_context(business),
            },
        )


class AdminAIHAffiliationView(SMEAdminRequired, View):
    """FRSME-OBR034 — administrator sets or corrects a business's AIH
    affiliation from the backend (bulk cohort onboarding, or where the
    entrepreneur can't complete affiliation themselves)."""

    template_name = "smehub/onboarding/aih_affiliation_admin.html"

    def _get_business(self, pk):
        return get_object_or_404(
            Business.objects.select_related("entrepreneur__user").prefetch_related(
                "affiliations__institution",
            ),
            pk=pk,
        )

    def _ctx(self, business, form):
        return {
            "business": business,
            "form": form,
            "affiliations": business.affiliations.select_related("institution"),
        }

    def get(self, request, pk: int):
        business = self._get_business(pk)
        return render(request, self.template_name, self._ctx(business, AIHAffiliationForm()))

    def post(self, request, pk: int):
        business = self._get_business(pk)
        form = AIHAffiliationForm(request.POST)
        if not form.is_valid():
            return render(request, self.template_name, self._ctx(business, form), status=400)
        cd = form.cleaned_data
        services.set_aih_affiliation_by_admin(
            business=business,
            institution_id=cd["institution"].pk,
            nature=cd["nature"],
            programme_of_origin=cd.get("programme_of_origin", ""),
            is_primary=cd.get("is_primary", True),
            admin=request.user,
        )
        messages.success(
            request,
            f"AIH affiliation set for {business.business_id} — {cd['institution'].name}.",
        )
        return redirect("smehub_onboarding:business_detail_admin", pk=pk)


@require_POST
@login_required
def verify_business_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    business = get_object_or_404(Business, pk=pk)
    services.verify_business(business, by_user=request.user)
    messages.success(request, f"Business {business.business_id} verified.")
    return redirect("smehub_onboarding:business_detail_admin", pk=pk)


@require_POST
@login_required
def reject_business_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    business = get_object_or_404(Business, pk=pk)
    form = RejectionReasonForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Reason required.")
        return redirect("smehub_onboarding:business_detail_admin", pk=pk)
    services.reject_business(business, by_user=request.user, reason=form.cleaned_data["reason"])
    messages.success(request, f"Business {business.business_id} rejected.")
    return redirect("smehub_onboarding:business_detail_admin", pk=pk)


@require_POST
@login_required
def revoke_business_view(request, pk: int):
    if not _is_sme_admin(request.user):
        return HttpResponse(status=403)
    business = get_object_or_404(Business, pk=pk)
    form = RejectionReasonForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Reason required.")
        return redirect("smehub_onboarding:business_detail_admin", pk=pk)
    services.revoke_business(business, by_user=request.user, reason=form.cleaned_data["reason"])
    messages.success(request, f"Business {business.business_id} revoked.")
    return redirect("smehub_onboarding:business_detail_admin", pk=pk)


# ---------------------------------------------------------------------------
# Backend manual register  (FRSME-OBR006)
# ---------------------------------------------------------------------------

class BackendRegisterView(SMEAdminRequired, View):
    template_name = "smehub/onboarding/backend_register.html"

    def get(self, request):
        return render(request, self.template_name, {"form": BackendRegisterForm()})

    def post(self, request):
        form = BackendRegisterForm(request.POST)
        if not form.is_valid():
            return render(request, self.template_name, {"form": form}, status=400)
        try:
            user = services.admin_backend_register(
                admin_user=request.user,
                email=form.cleaned_data["email"],
                full_name=form.cleaned_data["full_name"],
                role=form.cleaned_data["role"],
                country=form.cleaned_data["country"],
                organisation=form.cleaned_data["organisation"],
                phone=form.cleaned_data["phone"],
                initial_password=form.cleaned_data.get("initial_password") or None,
            )
        except services.DuplicateEmailError:
            form.add_error("email", "User with this email already exists.")
            return render(request, self.template_name, {"form": form}, status=400)
        messages.success(request, f"User {user.email} registered.")
        return redirect("smehub_onboarding:entrepreneur_queue")


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

from apps.core.permissions.roles import UserRole


def _is_sme_admin(user) -> bool:
    if not user.is_authenticated:
        return False
    if user.is_superuser:
        return True
    return user.role in (UserRole.SYSTEM_ADMIN, UserRole.ADMIN, UserRole.SME_ADMIN)


def _can_finalize_vetting(user) -> bool:
    """PRD §6.1/6.2: Programme Officer records the final decision; admins also.

    Approve / reject / request-revisions on an Innovation submission. Mirrors
    the SP2 incubation pattern where Programme Officers are the named decision
    actors and admins retain access.
    """
    if not user.is_authenticated:
        return False
    if user.is_superuser:
        return True
    return user.role in (
        UserRole.SYSTEM_ADMIN,
        UserRole.ADMIN,
        UserRole.SME_ADMIN,
        UserRole.PROGRAMME_OFFICER,
    )


# ---------------------------------------------------------------------------
# Innovation onboarding & vetting (Phase 6.1)
# ---------------------------------------------------------------------------


class InnovationCatalogueView(LoginRequiredMixin, ListView):
    """Authenticated list of approved + published innovations.

    The catalogue is "public" in the sense that any authenticated platform
    user can browse it — gated to authenticated users only (matches the
    existing marketplace ProductCatalogueView pattern).
    """

    template_name = "smehub/onboarding/innovation/innovation_list.html"
    context_object_name = "innovations"
    paginate_by = 24

    def get_queryset(self):
        qs = Innovation.objects.filter(
            is_public=True,
            lifecycle_status=Innovation.Status.APPROVED,
        ).select_related("business", "business__entrepreneur", "business__entrepreneur__user")
        q = self.request.GET.get("q", "").strip()
        if q:
            from django.db.models import Q

            qs = qs.filter(Q(title__icontains=q) | Q(description__icontains=q) | Q(sector__icontains=q))
        sector = self.request.GET.get("sector", "").strip()
        if sector:
            qs = qs.filter(sector__iexact=sector)
        return qs.order_by("-published_at")

    def get_context_data(self, **kwargs):
        from apps.smehub._shared.choices import SECTORS

        ctx = super().get_context_data(**kwargs)
        ctx["q"] = self.request.GET.get("q", "")
        ctx["sector"] = self.request.GET.get("sector", "")
        ctx["sector_choices"] = SECTORS
        return ctx


class InnovationDetailView(LoginRequiredMixin, View):
    template_name = "smehub/onboarding/innovation/innovation_detail.html"

    def get(self, request, pk):
        inv = get_object_or_404(
            Innovation.objects.select_related(
                "business", "business__entrepreneur__user",
            ),
            pk=pk,
        )
        # Public visibility gate.
        is_owner = request.user.is_authenticated and (
            getattr(inv.business.entrepreneur, "user_id", None) == request.user.pk
        )
        is_admin = _is_sme_admin(request.user)
        if not inv.is_public and not is_owner and not is_admin:
            raise PermissionDenied("Innovation is not public.")
        return render(
            request,
            self.template_name,
            {"innovation": inv, "is_owner": is_owner, "is_admin": is_admin},
        )


class MyInnovationsView(EntrepreneurRequired, ListView):
    template_name = "smehub/onboarding/innovation/my_innovations.html"
    context_object_name = "innovations"
    paginate_by = 24

    def get_queryset(self):
        return (
            Innovation.objects.filter(business__entrepreneur__user=self.request.user)
            .select_related("business")
            .order_by("-created_at")
        )

    def get_context_data(self, **kwargs):
        from django.db.models import Count

        ctx = super().get_context_data(**kwargs)
        ctx["businesses"] = Business.objects.filter(
            entrepreneur__user=self.request.user,
            verification_status=Business.Status.VERIFIED,
        )
        # Pipeline counts shown at the top of the page so the entrepreneur can
        # see at a glance where their submissions sit in the workflow.
        raw = dict(
            Innovation.objects.filter(business__entrepreneur__user=self.request.user)
            .values_list("lifecycle_status")
            .annotate(c=Count("pk"))
        )
        ctx["status_counts"] = [
            ("Draft",                raw.get("draft", 0),                "badge-neutral"),
            ("Submitted",            raw.get("submitted", 0),            "badge-primary"),
            ("Under vetting",        raw.get("under_vetting", 0),        "badge-amber"),
            ("Revisions requested", raw.get("revisions_requested", 0), "badge-amber"),
            ("Approved",             raw.get("approved", 0),             "badge-green"),
            ("Rejected",             raw.get("rejected", 0),             "badge-red"),
            ("Withdrawn",            raw.get("withdrawn", 0),            "badge-neutral"),
        ]
        return ctx


_INNOVATION_WIZARD_STEPS_CREATE = [
    {"n": 1, "title": "Business"},
    {"n": 2, "title": "About"},
    {"n": 3, "title": "Problem & Market"},
    {"n": 4, "title": "Team"},
    {"n": 5, "title": "Documents"},
]
_INNOVATION_WIZARD_STEPS_EDIT = [
    {"n": 1, "title": "About"},
    {"n": 2, "title": "Problem & Market"},
    {"n": 3, "title": "Team"},
    {"n": 4, "title": "Documents"},
]


class InnovationCreateView(EntrepreneurRequired, View):
    template_name = "smehub/onboarding/innovation/submit_innovation.html"

    def _get_business(self, request):
        business_id = request.POST.get("business") or request.GET.get("business")
        if not business_id:
            raise PermissionDenied("Pick a verified business first.")
        return get_object_or_404(
            Business,
            pk=business_id,
            entrepreneur__user=request.user,
            verification_status=Business.Status.VERIFIED,
        )

    def get(self, request):
        form = InnovationDraftForm()
        businesses = Business.objects.filter(
            entrepreneur__user=request.user,
            verification_status=Business.Status.VERIFIED,
        )
        return render(
            request,
            self.template_name,
            {
                "form": form,
                "businesses": businesses,
                "innovation": None,
                "wizard_steps": _INNOVATION_WIZARD_STEPS_CREATE,
            },
        )

    def post(self, request):
        business = self._get_business(request)
        form = InnovationDraftForm(request.POST, request.FILES)
        if not form.is_valid():
            return render(
                request,
                self.template_name,
                {
                    "form": form,
                    "businesses": Business.objects.filter(entrepreneur__user=request.user),
                    "innovation": None,
                    "wizard_steps": _INNOVATION_WIZARD_STEPS_CREATE,
                },
                status=400,
            )
        try:
            inv = services.create_innovation_draft(
                business=business,
                by_user=request.user,
                **{k: v for k, v in form.cleaned_data.items() if v is not None},
            )
        except services.InnovationStateError as exc:
            messages.error(request, str(exc))
            return redirect("smehub_onboarding:my_innovations")
        if request.POST.get("action") == "submit":
            services.submit_innovation(inv, by_user=request.user)
            messages.success(request, "Innovation submitted for vetting.")
        else:
            messages.success(request, "Draft saved.")
        return redirect("smehub_onboarding:innovation_edit", pk=inv.pk)


class InnovationUpdateView(EntrepreneurRequired, View):
    template_name = "smehub/onboarding/innovation/submit_innovation.html"

    def _get_innovation(self, request, pk):
        return get_object_or_404(
            Innovation.objects.select_related("business"),
            pk=pk,
            business__entrepreneur__user=request.user,
        )

    def get(self, request, pk):
        inv = self._get_innovation(request, pk)
        form = InnovationDraftForm(instance=inv)
        return render(
            request,
            self.template_name,
            {
                "form": form,
                "innovation": inv,
                "businesses": [inv.business],
                "wizard_steps": _INNOVATION_WIZARD_STEPS_EDIT,
            },
        )

    def post(self, request, pk):
        inv = self._get_innovation(request, pk)
        form = InnovationDraftForm(request.POST, request.FILES, instance=inv)
        if not form.is_valid():
            return render(
                request,
                self.template_name,
                {
                    "form": form,
                    "innovation": inv,
                    "businesses": [inv.business],
                    "wizard_steps": _INNOVATION_WIZARD_STEPS_EDIT,
                },
                status=400,
            )
        try:
            services.update_innovation_draft(
                inv,
                by_user=request.user,
                **{k: v for k, v in form.cleaned_data.items() if v is not None},
            )
        except services.InnovationStateError as exc:
            messages.error(request, str(exc))
        if request.POST.get("action") == "submit":
            try:
                services.submit_innovation(inv, by_user=request.user)
                messages.success(request, "Innovation submitted for vetting.")
            except Exception as exc:  # noqa: BLE001
                messages.error(request, f"Could not submit: {exc}")
        else:
            messages.success(request, "Draft saved.")
        return redirect("smehub_onboarding:innovation_edit", pk=inv.pk)


@login_required
@require_POST
def withdraw_innovation_view(request, pk):
    inv = get_object_or_404(
        Innovation, pk=pk, business__entrepreneur__user=request.user,
    )
    services.withdraw_innovation(
        inv, by_user=request.user, reason=request.POST.get("reason", ""),
    )
    messages.success(request, "Innovation withdrawn.")
    return redirect("smehub_onboarding:my_innovations")


class VettingQueueView(EvaluatorRequired, ListView):
    template_name = "smehub/onboarding/innovation/vetting_queue.html"
    context_object_name = "innovations"
    paginate_by = 25

    def get_queryset(self):
        qs = Innovation.objects.filter(
            lifecycle_status__in=[
                Innovation.Status.SUBMITTED,
                Innovation.Status.UNDER_VETTING,
                Innovation.Status.REVISIONS_REQUESTED,
            ],
        ).select_related("business")
        only_mine = self.request.GET.get("mine") == "1"
        if only_mine and not _is_sme_admin(self.request.user):
            qs = qs.filter(evaluator_assignments__evaluator=self.request.user).distinct()
        return qs.order_by("submitted_at", "-created_at")


class VettingFormView(EvaluatorRequired, View):
    template_name = "smehub/onboarding/innovation/vetting_form.html"

    def get(self, request, pk):
        inv = get_object_or_404(Innovation, pk=pk)
        is_admin = _is_sme_admin(request.user)
        can_finalize = _can_finalize_vetting(request.user)
        assignment = EvaluatorAssignment.objects.filter(
            innovation=inv, evaluator=request.user,
        ).first()
        if assignment is None and not can_finalize:
            raise PermissionDenied("You are not assigned to this innovation.")
        record = inv.vetting_records.filter(closed_at__isnull=True).order_by("-round_no").first()
        my_scores = (
            VettingScore.objects.filter(vetting_record=record, evaluator=request.user)
            if record
            else VettingScore.objects.none()
        )
        ctx = {
            "innovation": inv,
            "vetting_record": record,
            "my_scores": list(my_scores),
            "score_form": VettingScoreForm(),
            "recommendation_form": VettingRecommendationForm(),
            "decision_form": VettingDecisionForm() if can_finalize else None,
            "assign_form": EvaluatorAssignmentForm(
                evaluator_qs=_evaluator_pool(),
            ) if is_admin else None,
            "is_admin": is_admin,
            "can_finalize": can_finalize,
        }
        return render(request, self.template_name, ctx)


@login_required
@require_POST
def submit_score_view(request, pk):
    inv = get_object_or_404(Innovation, pk=pk)
    if not EvaluatorAssignment.objects.filter(innovation=inv, evaluator=request.user).exists():
        raise PermissionDenied("Only assigned evaluators can score.")
    form = VettingScoreForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Invalid score.")
        return redirect("smehub_onboarding:innovation_vetting", pk=pk)
    try:
        services.score_vetting_dimension(
            innovation=inv,
            evaluator=request.user,
            dimension=form.cleaned_data["dimension"],
            score=form.cleaned_data["score"],
            comments=form.cleaned_data.get("comments", ""),
        )
    except (services.InnovationStateError, ValueError) as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Score saved.")
    return redirect("smehub_onboarding:innovation_vetting", pk=pk)


@login_required
@require_POST
def submit_recommendation_view(request, pk):
    inv = get_object_or_404(Innovation, pk=pk)
    if not EvaluatorAssignment.objects.filter(innovation=inv, evaluator=request.user).exists():
        raise PermissionDenied("Not assigned to this innovation.")
    form = VettingRecommendationForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Pick a recommendation.")
        return redirect("smehub_onboarding:innovation_vetting", pk=pk)
    services.submit_vetting_recommendation(
        innovation=inv,
        evaluator=request.user,
        recommendation=form.cleaned_data["recommendation"],
        summary=form.cleaned_data.get("summary", ""),
    )
    messages.success(request, "Recommendation submitted.")
    return redirect("smehub_onboarding:innovation_vetting", pk=pk)


@login_required
@require_POST
def assign_evaluator_view(request, pk):
    if not _is_sme_admin(request.user):
        raise PermissionDenied("Admin only.")
    inv = get_object_or_404(Innovation, pk=pk)
    form = EvaluatorAssignmentForm(request.POST, evaluator_qs=_evaluator_pool())
    if not form.is_valid():
        messages.error(request, "Pick an evaluator.")
        return redirect("smehub_onboarding:innovation_vetting", pk=pk)
    try:
        services.assign_evaluator(
            inv,
            evaluator=form.cleaned_data["evaluator"],
            by_user=request.user,
            due_at=form.cleaned_data.get("due_at"),
        )
    except services.InnovationStateError as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Evaluator assigned.")
    return redirect("smehub_onboarding:innovation_vetting", pk=pk)


@login_required
@require_POST
def vet_application_view(request, pk):
    if not _can_finalize_vetting(request.user):
        raise PermissionDenied("Only programme officers and admins can finalize vetting.")
    inv = get_object_or_404(Innovation, pk=pk)
    form = VettingDecisionForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Provide a decision and supporting text.")
        return redirect("smehub_onboarding:innovation_vetting", pk=pk)
    try:
        services.vet_application(
            inv,
            by_user=request.user,
            decision=form.cleaned_data["decision"],
            feedback=form.cleaned_data.get("feedback", ""),
            reason=form.cleaned_data.get("reason", ""),
        )
    except (services.InnovationStateError, ValueError) as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Decision recorded.")
    return redirect("smehub_onboarding:innovation_vetting", pk=pk)


@login_required
@require_POST
def onboard_innovation_view(request, pk):
    if not _is_sme_admin(request.user):
        raise PermissionDenied("Admin only.")
    inv = get_object_or_404(Innovation, pk=pk)
    try:
        services.onboard(inv, by_user=request.user)
    except ValueError as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Innovation published to the public catalogue.")
    return redirect("smehub_onboarding:innovation_detail", pk=pk)


def _evaluator_pool():
    """Pool of users eligible to be assigned as evaluators."""
    from django.db.models import Q

    return User.objects.filter(
        is_active=True,
    ).filter(
        Q(role__in=[
            UserRole.JUDGE,
            UserRole.PROGRAMME_OFFICER,
            UserRole.REVIEWER,
            UserRole.SME_ADMIN,
            UserRole.ADMIN,
            UserRole.SYSTEM_ADMIN,
        ]) | Q(is_superuser=True)
    ).order_by("email")


User = get_user_model()


# ---------------------------------------------------------------------------
# Imported-data UI surfaces (FAQ · Mentor directory · Newsletter)
# ---------------------------------------------------------------------------


class FaqListView(ListView):
    """Public help / FAQ page (onboarding.Faq — published entries only)."""

    template_name = "smehub/onboarding/faq_list.html"
    context_object_name = "faqs"

    def get_queryset(self):
        return Faq.objects.filter(is_published=True).order_by("order", "id")


class MentorDirectoryView(LoginRequiredMixin, ListView):
    """Browse verified mentors, filterable by mentorship category."""

    template_name = "smehub/onboarding/mentor_directory.html"
    context_object_name = "mentors"
    paginate_by = 24

    def get_queryset(self):
        qs = (
            MentorProfile.objects.filter(
                verification_status=MentorProfile.Status.VERIFIED
            )
            .select_related("user")
            .prefetch_related("categories")
        )
        q = self.request.GET.get("q", "").strip()
        if q:
            qs = qs.filter(
                Q(user__first_name__icontains=q)
                | Q(user__last_name__icontains=q)
                | Q(current_company__icontains=q)
                | Q(current_position__icontains=q)
                | Q(bio__icontains=q)
            )
        category = self.request.GET.get("category", "").strip()
        if category:
            qs = qs.filter(categories__id=category)
        return qs.distinct().order_by("user__first_name", "user__last_name")

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["q"] = self.request.GET.get("q", "")
        ctx["current_category"] = self.request.GET.get("category", "")
        ctx["categories"] = MentorshipCategory.objects.order_by("name")
        ctx["mentor_count"] = self.get_queryset().count()
        return ctx


class MentorDetailView(LoginRequiredMixin, View):
    template_name = "smehub/onboarding/mentor_detail.html"

    def get(self, request, pk):
        from django.contrib.contenttypes.models import ContentType

        mentor = get_object_or_404(
            MentorProfile.objects.select_related("user").prefetch_related("categories"),
            pk=pk,
        )
        if mentor.verification_status != MentorProfile.Status.VERIFIED and not _is_sme_admin(request.user):
            raise PermissionDenied("Mentor profile is not public.")
        my_requests = MeetingRequest.objects.filter(
            requester=request.user,
            recipient_content_type=ContentType.objects.get_for_model(MentorProfile),
            recipient_object_id=mentor.pk,
        ).order_by("-created_at")
        pending_request = next(
            (m for m in my_requests if m.status == MeetingRequest.Status.REQUEST), None
        )
        return render(
            request,
            self.template_name,
            {
                "mentor": mentor,
                "meeting_form": MeetingRequestForm(),
                "my_requests": my_requests,
                "pending_request": pending_request,
                "can_request": mentor.user_id != request.user.pk,
            },
        )


class RequestMentorMeetingView(LoginRequiredMixin, View):
    """POST-only — creates a MeetingRequest to a mentor from mentor_detail."""

    def post(self, request, pk):
        mentor = get_object_or_404(MentorProfile, pk=pk)
        form = MeetingRequestForm(request.POST)
        if not form.is_valid():
            messages.error(request, "Please describe what you would like to discuss.")
            return redirect("smehub_onboarding:mentor_detail", pk=pk)
        try:
            services.request_mentor_meeting(
                requester=request.user,
                mentor=mentor,
                agenda=form.cleaned_data["agenda"],
                meeting_time=form.cleaned_data.get("meeting_time"),
            )
        except services.MeetingRequestError as exc:
            messages.error(request, str(exc))
            return redirect("smehub_onboarding:mentor_detail", pk=pk)
        messages.success(
            request,
            f"Meeting request sent to {mentor.user.get_full_name() or mentor.user.email}. "
            "You'll be notified when they respond.",
        )
        return redirect("smehub_onboarding:mentor_detail", pk=pk)


class MyMeetingRequestsView(LoginRequiredMixin, ListView):
    """The requests the signed-in user has sent to mentors."""

    template_name = "smehub/onboarding/my_meeting_requests.html"
    context_object_name = "meetings"
    paginate_by = 20

    def get_queryset(self):
        return (
            MeetingRequest.objects.filter(requester=self.request.user)
            .select_related("recipient_content_type")
            .prefetch_related("recipient")
            .order_by("-created_at")
        )


@require_POST
@login_required
def cancel_meeting_request_view(request, pk: int):
    mr = get_object_or_404(MeetingRequest, pk=pk, requester=request.user)
    try:
        services.cancel_meeting_request(mr, by_user=request.user)
    except services.MeetingRequestError as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Meeting request cancelled.")
    return redirect("smehub_onboarding:my_meeting_requests")


class MentorMeetingInboxView(MentorRequired, ListView):
    """Requests addressed to the signed-in mentor's profile."""

    template_name = "smehub/onboarding/mentor_meeting_inbox.html"
    context_object_name = "meetings"
    paginate_by = 20

    def get_queryset(self):
        from django.contrib.contenttypes.models import ContentType

        mentor = MentorProfile.objects.filter(user=self.request.user).first()
        if mentor is None:
            return MeetingRequest.objects.none()
        return (
            MeetingRequest.objects.filter(
                recipient_content_type=ContentType.objects.get_for_model(MentorProfile),
                recipient_object_id=mentor.pk,
            )
            .select_related("requester")
            .order_by("-created_at")
        )

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["respond_form"] = MeetingRespondForm()
        ctx["pending_count"] = self.get_queryset().filter(
            status=MeetingRequest.Status.REQUEST
        ).count()
        return ctx


@require_POST
@login_required
def respond_meeting_request_view(request, pk: int):
    mentor = MentorProfile.objects.filter(user=request.user).first()
    if mentor is None:
        return HttpResponse(status=403)
    mr = get_object_or_404(
        MeetingRequest,
        pk=pk,
        recipient_type=MeetingRequest.RecipientType.MENTOR,
        recipient_object_id=mentor.pk,
    )
    form = MeetingRespondForm(request.POST)
    if not form.is_valid():
        messages.error(request, "Could not record your response — check the link and try again.")
        return redirect("smehub_onboarding:mentor_meeting_inbox")
    try:
        services.respond_meeting_request(
            mr,
            by_user=request.user,
            accept=form.cleaned_data["decision"] == "accept",
            meeting_link=form.cleaned_data.get("meeting_link", ""),
            reason=form.cleaned_data.get("reason", ""),
        )
    except services.MeetingRequestError as exc:
        messages.error(request, str(exc))
    else:
        messages.success(request, "Response sent — the requester has been notified.")
    return redirect("smehub_onboarding:mentor_meeting_inbox")


class CommunityArchiveView(LoginRequiredMixin, ListView):
    """Read-only archive of the legacy SME-Hub community forum."""

    template_name = "smehub/onboarding/community_archive.html"
    context_object_name = "discussions"

    def get_queryset(self):
        from apps.smehub.onboarding.models import CommunityDiscussion

        return (
            CommunityDiscussion.objects.all()
            .select_related("author")
            .prefetch_related("posts__author")
            .order_by("-created_at")
        )


class NewsletterSubscriberListView(SMEAdminRequired, ListView):
    """Admin-only newsletter subscriber roster with CSV export."""

    template_name = "smehub/onboarding/newsletter_subscribers.html"
    context_object_name = "subscribers"
    paginate_by = 50

    def get_queryset(self):
        qs = NewsletterSubscriber.objects.all().order_by("email")
        q = self.request.GET.get("q", "").strip()
        if q:
            qs = qs.filter(email__icontains=q)
        status = self.request.GET.get("status", "").strip()
        if status:
            qs = qs.filter(status=status)
        return qs

    def get(self, request, *args, **kwargs):
        if request.GET.get("export") == "csv":
            return self._export_csv()
        return super().get(request, *args, **kwargs)

    def _export_csv(self):
        import csv

        response = HttpResponse(content_type="text/csv")
        response["Content-Disposition"] = 'attachment; filename="newsletter_subscribers.csv"'
        writer = csv.writer(response)
        writer.writerow(["email", "status", "subscribed_at"])
        for sub in self.get_queryset():
            writer.writerow([sub.email, sub.status, sub.subscribed_at or ""])
        return response

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["q"] = self.request.GET.get("q", "")
        ctx["current_status"] = self.request.GET.get("status", "")
        ctx["total_count"] = NewsletterSubscriber.objects.count()
        ctx["subscribed_count"] = NewsletterSubscriber.objects.filter(
            status=NewsletterSubscriber.Status.SUBSCRIBED
        ).count()
        ctx["status_choices"] = NewsletterSubscriber.Status.choices
        return ctx


class MeetingRequestListView(SMEAdminRequired, ListView):
    """Admin oversight of direct meeting requests (mentor + business recipients)."""

    template_name = "smehub/onboarding/meeting_requests.html"
    context_object_name = "meetings"
    paginate_by = 25

    def get_queryset(self):
        qs = (
            MeetingRequest.objects.all()
            .select_related("requester", "recipient_content_type")
            .prefetch_related("recipient")
            .order_by("-meeting_time", "-created_at")
        )
        status = self.request.GET.get("status", "").strip()
        if status:
            qs = qs.filter(status=status)
        recipient_type = self.request.GET.get("recipient", "").strip()
        if recipient_type:
            qs = qs.filter(recipient_type=recipient_type)
        return qs

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["current_status"] = self.request.GET.get("status", "")
        ctx["current_recipient"] = self.request.GET.get("recipient", "")
        ctx["total_count"] = MeetingRequest.objects.count()
        ctx["status_choices"] = MeetingRequest.Status.choices
        ctx["recipient_choices"] = MeetingRequest.RecipientType.choices
        return ctx


class SmeAnalyticsView(SMEAdminRequired, TemplateView):
    """Cohort M&E snapshot — youth / gender / sector / country / stage mix.

    Aggregates over the Business + EntrepreneurProfile records (primarily the
    imported cohort) to feed the SP6 / M&EL reporting picture.
    """

    template_name = "smehub/onboarding/sme_analytics.html"

    def get_context_data(self, **kwargs):
        from datetime import date

        from django.db.models import Count, Sum

        from apps.core.authentication.models import UserProfile

        ctx = super().get_context_data(**kwargs)
        businesses = Business.objects.all()
        total = businesses.count()
        ctx["total_businesses"] = total

        # Sector / stage / country distributions (top slices).
        ctx["sector_dist"] = list(
            businesses.exclude(sector="").values("sector")
            .annotate(n=Count("id")).order_by("-n")[:10]
        )
        from apps.smehub.onboarding.models import BusinessStage

        stage_labels = dict(BusinessStage.choices)
        ctx["stage_dist"] = [
            {**row, "label": stage_labels.get(row["business_stage"], row["business_stage"])}
            for row in businesses.exclude(business_stage="").values("business_stage")
            .annotate(n=Count("id")).order_by("-n")
        ]
        ctx["country_dist"] = list(
            businesses.exclude(country="").values("country")
            .annotate(n=Count("id")).order_by("-n")[:12]
        )

        # Employment (gender-disaggregated M&E).
        emp = businesses.aggregate(
            total_emp=Sum("total_employees"), female_emp=Sum("female_employees")
        )
        total_emp = emp["total_emp"] or 0
        female_emp = emp["female_emp"] or 0
        ctx["total_employees"] = total_emp
        ctx["female_employees"] = female_emp
        ctx["female_pct"] = round(female_emp * 100 / total_emp) if total_emp else 0

        # Youth + student signals (entrepreneurs).
        entrepreneurs = EntrepreneurProfile.objects.all()
        ctx["total_entrepreneurs"] = entrepreneurs.count()
        ctx["student_count"] = entrepreneurs.filter(is_student=True).count()
        # Youth: AU definition ≤ 35 (UserProfile.YOUTH_AGE_THRESHOLD), from DOB.
        dobs = UserProfile.objects.filter(
            user__smehub_entrepreneur__isnull=False, date_of_birth__isnull=False
        ).values_list("date_of_birth", flat=True)
        today = date.today()
        youth = with_dob = 0
        for dob in dobs:
            age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
            with_dob += 1
            if age <= UserProfile.YOUTH_AGE_THRESHOLD:
                youth += 1
        ctx["youth_count"] = youth
        ctx["youth_with_dob"] = with_dob
        ctx["youth_pct"] = round(youth * 100 / with_dob) if with_dob else 0

        ctx["max_sector"] = max((d["n"] for d in ctx["sector_dist"]), default=1)
        ctx["max_country"] = max((d["n"] for d in ctx["country_dist"]), default=1)
        ctx["max_stage"] = max((d["n"] for d in ctx["stage_dist"]), default=1)
        return ctx
