"""SP2 forms — programmes, applications, judging, cohorts, sessions."""
from __future__ import annotations

from decimal import Decimal

from django import forms
from django.forms import inlineformset_factory

from apps.core.countries import CountryMultipleChoiceField
from apps.smehub._shared.choices import (
    BUSINESS_STAGES,
    COUNTRIES,
    SECTORS,
)
from apps.smehub.incubation.models import (
    Application,
    ApplicationDocument,
    Cohort,
    MentorAssignment,
    MentorSession,
    Programme,
    RubricSection,
    ScoringRubric,
)

INPUT_BASE = (
    "block w-full rounded-lg border border-ruforum-border bg-white px-3 py-2 "
    "text-sm text-ruforum-text shadow-sm placeholder-ruforum-text-muted/70 "
    "focus:border-ruforum-primary focus:outline-none focus:ring-1 focus:ring-ruforum-primary"
)
TEXTAREA_BASE = INPUT_BASE + " min-h-[7rem]"
SELECT_BASE = INPUT_BASE
CHECKBOX_BASE = "h-4 w-4 rounded border-ruforum-border text-ruforum-primary focus:ring-ruforum-primary"
FILE_INPUT_BASE = (
    "block w-full text-sm text-ruforum-text file:mr-3 file:rounded-lg file:border-0 "
    "file:bg-ruforum-primary/10 file:px-3 file:py-2 file:text-sm file:font-semibold "
    "file:text-ruforum-primary hover:file:bg-ruforum-primary/20 file:cursor-pointer"
)


def _s2_single(*, placeholder: str = "") -> dict:
    attrs = {"class": SELECT_BASE, "data-s2": ""}
    if placeholder:
        attrs["data-s2-placeholder"] = placeholder
    return attrs


def _s2_multi(*, placeholder: str) -> dict:
    return {
        "class": SELECT_BASE + " min-h-[2.5rem]",
        "data-s2": "",
        "data-s2-placeholder": placeholder,
    }


class _JudgeChoiceField(forms.ModelMultipleChoiceField):
    """Friendlier labels: 'Full Name · email' falls back to email."""

    def label_from_instance(self, obj):
        full = obj.get_full_name() if hasattr(obj, "get_full_name") else ""
        full = (full or "").strip()
        return f"{full} · {obj.email}" if full else obj.email


def _judge_queryset():
    """Active judge-role users, ordered for the programme roster picker."""
    from django.contrib.auth import get_user_model

    from apps.core.permissions.roles import UserRole

    return (
        get_user_model()
        .objects.filter(role=UserRole.JUDGE, is_active=True)
        .order_by("first_name", "last_name", "email")
    )


# ---------------------------------------------------------------------------
# Programme
# ---------------------------------------------------------------------------

class ProgrammeForm(forms.ModelForm):
    target_sectors = forms.MultipleChoiceField(
        required=False,
        choices=SECTORS,
        widget=forms.SelectMultiple(attrs=_s2_multi(placeholder="Pick eligible sectors")),
        label="Target sectors",
    )
    target_stages = forms.MultipleChoiceField(
        required=False,
        choices=BUSINESS_STAGES,
        widget=forms.SelectMultiple(attrs=_s2_multi(placeholder="Pick eligible stages")),
        label="Target business stages",
    )
    eligibility_countries = CountryMultipleChoiceField(
        required=False,
        choices=COUNTRIES,
        widget=forms.SelectMultiple(attrs=_s2_multi(placeholder="Leave blank for any country")),
        label="Eligible countries",
        help_text="Leave blank to accept entrepreneurs from any RUFORUM country.",
    )
    aih_locations = forms.CharField(
        required=False,
        widget=forms.TextInput(attrs={"class": INPUT_BASE, "placeholder": "Comma-separated AIH names"}),
        label="AIH locations / venues",
        help_text="Free-form list — separate venues with commas.",
    )
    required_document_labels = forms.CharField(
        required=False,
        widget=forms.TextInput(
            attrs={"class": INPUT_BASE, "placeholder": "e.g. Business registration, Financial statements"},
        ),
        label="Required supporting documents",
        help_text="Applicants must attach each of these before submitting — separate with commas. Leave blank for none.",
    )

    class Meta:
        model = Programme
        fields = [
            "title",
            "slug",
            "objectives",
            "target_sectors",
            "target_stages",
            "eligibility_countries",
            "cohort_size",
            "duration_weeks",
            "aih_locations",
            "required_document_labels",
            "min_mentorship_sessions",
            "attendance_threshold_pct",
            "application_opens_at",
            "application_deadline",
            "selection_method",
            "judges",
            "scoring_deadline",
            "reminder_days_before",
        ]
        widgets = {
            "title": forms.TextInput(attrs={"class": INPUT_BASE}),
            "slug": forms.TextInput(attrs={"class": INPUT_BASE}),
            "objectives": forms.Textarea(attrs={"class": TEXTAREA_BASE}),
            "cohort_size": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 1}),
            "duration_weeks": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 1}),
            "min_mentorship_sessions": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 0}),
            "attendance_threshold_pct": forms.NumberInput(
                attrs={"class": INPUT_BASE, "min": 0, "max": 100},
            ),
            "application_opens_at": forms.DateTimeInput(
                attrs={"class": INPUT_BASE, "type": "datetime-local"}, format="%Y-%m-%dT%H:%M",
            ),
            "application_deadline": forms.DateTimeInput(
                attrs={"class": INPUT_BASE, "type": "datetime-local"}, format="%Y-%m-%dT%H:%M",
            ),
            "selection_method": forms.Select(attrs=_s2_single(placeholder="Pick a method")),
            "scoring_deadline": forms.DateTimeInput(
                attrs={"class": INPUT_BASE, "type": "datetime-local"}, format="%Y-%m-%dT%H:%M",
            ),
            "reminder_days_before": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 0}),
            "judges": forms.SelectMultiple(attrs=_s2_multi(placeholder="Search and pick judges…")),
        }
        field_classes = {"judges": _JudgeChoiceField}

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for f in ("application_opens_at", "application_deadline", "scoring_deadline"):
            self.fields[f].input_formats = ["%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"]
        self.fields["judges"].queryset = _judge_queryset()
        self.fields["judges"].required = False
        # Render stored JSON lists back as comma-separated text on the edit form.
        if self.instance and self.instance.pk:
            if isinstance(self.instance.required_document_labels, list):
                self.fields["required_document_labels"].initial = ", ".join(
                    self.instance.required_document_labels,
                )
            if isinstance(self.instance.aih_locations, list):
                self.fields["aih_locations"].initial = ", ".join(self.instance.aih_locations)

    def clean_aih_locations(self):
        raw = self.cleaned_data.get("aih_locations") or ""
        entries = [v.strip() for v in raw.split(",") if v.strip()]
        if entries:
            # FRSME-INC008 — eligibility matches these venue names against the
            # Institution directory (tolerantly). An entry that matches no
            # institution can never be satisfied and silently locks every
            # entrepreneur out, so reject it here with the unmatched names.
            from apps.core.authentication.models import Institution
            from apps.smehub.incubation.services import aih_location_matches

            institution_names = list(Institution.objects.values_list("name", flat=True))
            unmatched = [
                entry
                for entry in entries
                if not any(aih_location_matches(entry, name) for name in institution_names)
            ]
            if unmatched:
                raise forms.ValidationError(
                    "These AIH locations don't match any institution in the directory: "
                    f"{', '.join(unmatched)}. Eligibility can only be met when each "
                    "location references a registered institution — check the spelling "
                    "or register the institution first.",
                )
        return entries

    def clean_required_document_labels(self):
        raw = self.cleaned_data.get("required_document_labels") or ""
        return [v.strip() for v in raw.split(",") if v.strip()]

    def clean_attendance_threshold_pct(self):
        value = self.cleaned_data.get("attendance_threshold_pct") or 0
        if value > 100:
            raise forms.ValidationError("Attendance threshold cannot exceed 100%.")
        return value


class RubricSectionForm(forms.ModelForm):
    class Meta:
        model = RubricSection
        fields = ["title", "instructions", "weight", "max_marks", "order"]
        widgets = {
            "title": forms.TextInput(attrs={"class": INPUT_BASE}),
            "instructions": forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 2}),
            "weight": forms.NumberInput(attrs={"class": INPUT_BASE, "step": "0.01", "min": "0", "max": "1"}),
            "max_marks": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 1}),
            "order": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 0}),
        }


RubricSectionFormSet = inlineformset_factory(
    ScoringRubric,
    RubricSection,
    form=RubricSectionForm,
    extra=1,
    can_delete=True,
)


# ---------------------------------------------------------------------------
# Application
# ---------------------------------------------------------------------------

class ApplicationForm(forms.ModelForm):
    class Meta:
        model = Application
        fields = [
            "business_description",
            "problem_statement",
            "traction",
            "funding_needs",
            "support_sought",
        ]
        widgets = {
            f: forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 4})
            for f in ["business_description", "problem_statement", "traction", "funding_needs", "support_sought"]
        }


class ApplicationDocumentForm(forms.ModelForm):
    """FRSME-INC010 — attach a single supporting document to an application."""

    class Meta:
        model = ApplicationDocument
        fields = ["label", "file"]
        widgets = {
            "label": forms.TextInput(
                attrs={"class": INPUT_BASE, "placeholder": "e.g. Business registration certificate"},
            ),
            "file": forms.ClearableFileInput(attrs={"class": FILE_INPUT_BASE}),
        }


class StartApplicationForm(forms.Form):
    """Choose which verified business to apply with."""

    business = forms.ModelChoiceField(
        queryset=None,
        widget=forms.Select(attrs=_s2_single(placeholder="Search your verified businesses…")),
        empty_label="Select a verified business",
    )

    def __init__(self, *args, business_qs=None, **kwargs):
        super().__init__(*args, **kwargs)
        if business_qs is not None:
            self.fields["business"].queryset = business_qs


# ---------------------------------------------------------------------------
# Judging
# ---------------------------------------------------------------------------

class JudgeScoreForm(forms.Form):
    """Dynamic form built from the rubric sections at runtime."""

    comments = forms.CharField(required=False, widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}))

    def __init__(self, *args, sections=None, initial_scores=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.sections = list(sections or [])
        for section in self.sections:
            initial = (initial_scores or {}).get(section.pk)
            self.fields[f"section_{section.pk}"] = forms.DecimalField(
                label=f"{section.title} (max {section.max_marks})",
                min_value=Decimal("0"),
                max_value=Decimal(str(section.max_marks)),
                decimal_places=2,
                required=True,
                initial=initial,
                widget=forms.NumberInput(
                    attrs={
                        # py-3 (≥44px touch target) overrides INPUT_BASE's py-2; py-3 is
                        # present in scanned templates so it survives the build.
                        "class": INPUT_BASE.replace("py-2", "py-3"),
                        "step": "0.5",
                        "min": "0",
                        "max": str(section.max_marks),
                        "aria-label": f"{section.title} score, out of {section.max_marks}",
                    },
                ),
            )

    def section_scores(self) -> dict[int, Decimal]:
        return {
            int(name.split("_", 1)[1]): value
            for name, value in self.cleaned_data.items()
            if name.startswith("section_") and value is not None
        }


class AssignJudgesForm(forms.Form):
    judges = _JudgeChoiceField(
        queryset=None,
        widget=forms.SelectMultiple(attrs=_s2_multi(placeholder="Search and pick judges…")),
        label="Pick judges",
    )

    def __init__(self, *args, judge_qs=None, **kwargs):
        super().__init__(*args, **kwargs)
        if judge_qs is not None:
            self.fields["judges"].queryset = judge_qs


class ReturnForInfoForm(forms.Form):
    message = forms.CharField(
        label="Message to applicant",
        widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
    )


# ---------------------------------------------------------------------------
# Cohort + selection
# ---------------------------------------------------------------------------

class CohortForm(forms.ModelForm):
    class Meta:
        model = Cohort
        fields = ["name", "start_date", "end_date", "venue"]
        widgets = {
            "name": forms.TextInput(attrs={"class": INPUT_BASE}),
            "start_date": forms.DateInput(attrs={"class": INPUT_BASE, "type": "date"}),
            "end_date": forms.DateInput(attrs={"class": INPUT_BASE, "type": "date"}),
            "venue": forms.TextInput(attrs={"class": INPUT_BASE}),
        }


class WithdrawForm(forms.Form):
    reason = forms.CharField(widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}))


class ManualCompleteForm(forms.Form):
    # FRSME-INC035 — a manual completion override requires a mandatory written
    # reason, recorded in the audit trail.
    notes = forms.CharField(
        required=True, widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
        label="Reason for manual completion",
        help_text="Why is this member being completed manually? This is recorded in the audit trail.",
    )


# ---------------------------------------------------------------------------
# Mentor + sessions
# ---------------------------------------------------------------------------

class MentorAssignmentForm(forms.Form):
    mentor = forms.ModelChoiceField(
        queryset=None,
        widget=forms.Select(attrs=_s2_single(placeholder="Search mentors…")),
        empty_label="Select a mentor",
    )
    notes = forms.CharField(required=False, widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 2}))

    def __init__(self, *args, mentor_qs=None, **kwargs):
        super().__init__(*args, **kwargs)
        if mentor_qs is not None:
            self.fields["mentor"].queryset = mentor_qs


class SessionForm(forms.ModelForm):
    class Meta:
        model = MentorSession
        fields = ["starts_at", "duration_minutes", "type", "location", "agenda"]
        widgets = {
            "starts_at": forms.DateTimeInput(
                attrs={"class": INPUT_BASE, "type": "datetime-local"}, format="%Y-%m-%dT%H:%M",
            ),
            "duration_minutes": forms.NumberInput(attrs={"class": INPUT_BASE, "min": 15, "step": 15}),
            "type": forms.Select(attrs=_s2_single(placeholder="Pick a session type")),
            "location": forms.TextInput(attrs={"class": INPUT_BASE}),
            "agenda": forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["starts_at"].input_formats = ["%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"]


class CompleteSessionForm(forms.Form):
    notes = forms.CharField(required=False, widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}))


class CancelSessionForm(forms.Form):
    reason = forms.CharField(widget=forms.TextInput(attrs={"class": INPUT_BASE}))


class ManualTrainingForm(forms.Form):
    title = forms.CharField(widget=forms.TextInput(attrs={"class": INPUT_BASE}))
    completed_at = forms.DateTimeField(
        required=False,
        widget=forms.DateTimeInput(attrs={"class": INPUT_BASE, "type": "datetime-local"}, format="%Y-%m-%dT%H:%M"),
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["completed_at"].input_formats = ["%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M"]


def _score_field(label: str) -> forms.IntegerField:
    return forms.IntegerField(
        label=label,
        min_value=1,
        max_value=5,
        widget=forms.NumberInput(attrs={
            "class": INPUT_BASE.replace("py-2", "py-3"),
            "min": 1,
            "max": 5,
            "step": 1,
            "aria-label": f"{label} readiness score, 1 (weak) to 5 (strong)",
        }),
        help_text="Score 1 (weak) – 5 (strong).",
    )


class ReadinessAssessmentForm(forms.Form):
    """Capture a Mentorship Readiness Assessment (PRD §5.X — Mentorship and
    Industry Advisory)."""

    market_positioning_score = _score_field("Market positioning")
    market_positioning_notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
    )
    technical_robustness_score = _score_field("Technical robustness")
    technical_robustness_notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
    )
    regulatory_compliance_score = _score_field("Regulatory compliance")
    regulatory_compliance_notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
    )
    overall_notes = forms.CharField(
        required=False,
        widget=forms.Textarea(attrs={"class": TEXTAREA_BASE, "rows": 3}),
    )
