"""Seed the alumni module with a substantial, realistic dataset.

Idempotent — safe to re-run. Creates alumni profiles both by promoting RIMS
graduation records (when any exist) and by synthesising direct demo alumni
across sectors, countries, and cohorts. Also seeds network groups, events,
registrations, mentorship pairings, career milestones, ORCID research
outputs, impact records, and monthly impact snapshots.
"""
from __future__ import annotations

import random
from datetime import date, timedelta
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils import timezone


# Expanded demo fixtures: 24 alumni spread across 8 countries and 5 sectors.
_DEMO_ALUMNI = [
    # (first, last, email, country, employer, position, sector, expertise, headline, orcid_id, programme, degree_type, grad_year, consent)
    ("Amani",    "Kagame",     "amani.kagame@alumni.example",    "RW", "Rwanda Agriculture Board",      "Senior Researcher",       "research",    ["agronomy","climate-resilience","soil-health"],    "Soil-health scientist advancing Rwanda's climate-smart agenda.", "0000-0001-1111-1111", "MSc Agronomy",           "msc", 2019, True),
    ("Bwanika",  "Sserwanga",  "bwanika.sserwanga@alumni.example","UG", "Makerere University",           "Lecturer",                "academia",    ["policy","extension","value-chain"],               "Lecturer and extension scholar at Makerere University.",         "0000-0001-2222-2222", "PhD Agricultural Economics","phd", 2020, True),
    ("Chinedu",  "Okafor",     "chinedu.okafor@alumni.example",   "NG", "IITA",                           "Research Fellow",         "research",    ["plant-breeding","cassava","genomics"],            "Plant breeder focusing on cassava improvement for West Africa.", "0000-0001-3333-3333", "PhD Plant Sciences",      "phd", 2018, True),
    ("Desta",    "Yohannes",   "desta.yohannes@alumni.example",   "ET", "Ministry of Agriculture",       "Policy Analyst",          "government",  ["policy","governance","food-security"],           "Informing national food-security policy with field evidence.",   "",                     "MSc Development Economics","msc", 2021, True),
    ("Esi",      "Mensah",     "esi.mensah@alumni.example",       "GH", "CSIR — Ghana",                  "Senior Scientist",        "research",    ["nutrition","food-systems","gender"],             "Interdisciplinary scholar linking nutrition to gender equity.",  "0000-0001-4444-4444", "PhD Nutrition",           "phd", 2017, True),
    ("Farida",   "Mwangi",     "farida.mwangi@alumni.example",    "KE", "CGIAR — ILRI",                   "Scientist",               "research",    ["livestock","climate-resilience","value-chain"], "Livestock systems researcher advancing pastoralist livelihoods.","0000-0001-5555-5555", "MSc Animal Science",      "msc", 2019, True),
    ("Gabriel",  "Mutombo",    "gabriel.mutombo@alumni.example",  "CD", "FAO DR Congo",                   "Country Officer",         "ngo",          ["policy","food-security","agribusiness"],        "Bridging FAO programs and smallholder farmers in DRC.",          "",                     "MSc Agribusiness",        "msc", 2020, True),
    ("Halima",   "Abdi",       "halima.abdi@alumni.example",      "SO", "World Food Programme",          "Programme Coordinator",   "ngo",          ["nutrition","gender","policy"],                  "Driving nutrition-sensitive programming in the Horn of Africa.","0000-0001-6666-6666", "MSc Nutrition",           "msc", 2022, True),
    ("Ibrahim",  "Cisse",      "ibrahim.cisse@alumni.example",    "SN", "ISRA",                            "Research Scientist",      "research",    ["crop-science","climate-resilience"],           "Sahelian crop scientist building drought-tolerant varieties.",   "0000-0001-7777-7777", "PhD Crop Sciences",       "phd", 2016, True),
    ("Juma",     "Nyamweru",   "juma.nyamweru@alumni.example",    "TZ", "Sokoine University of Agriculture","Dean of Research",        "academia",    ["policy","governance","research-management"],   "Academic leader championing open science in East Africa.",       "",                     "PhD Agricultural Sciences","phd", 2012, True),
    ("Kemi",     "Adebayo",    "kemi.adebayo@alumni.example",     "NG", "AgriTech Innovations Ltd.",     "Founder",                 "private",     ["agribusiness","digital-ag","entrepreneurship"],"AgTech founder scaling smallholder market linkages in Nigeria.","",                     "MSc Innovation",          "msc", 2021, True),
    ("Lemlem",   "Haile",      "lemlem.haile@alumni.example",     "ET", "Addis Ababa University",        "Assistant Professor",     "academia",    ["agronomy","soil-health","climate-resilience"], "Assistant professor studying tef agronomy in Ethiopia.",         "0000-0001-8888-8888", "PhD Agronomy",             "phd", 2015, True),
    ("Moses",    "Banda",      "moses.banda@alumni.example",      "ZM", "Zambia Agriculture Research Institute","Principal Researcher",   "research",    ["maize","plant-breeding","genomics"],            "Maize breeder improving smallholder resilience in Zambia.",      "0000-0001-9999-9999", "PhD Plant Breeding",      "phd", 2014, False),
    ("Nadia",    "Boukhari",   "nadia.boukhari@alumni.example",   "MA", "INRA Morocco",                  "Senior Researcher",       "research",    ["water","irrigation","climate-resilience"],     "Irrigation specialist optimising water use in the Maghreb.",    "",                     "PhD Water Management",     "phd", 2018, True),
    ("Ochieng",  "Otieno",     "ochieng.otieno@alumni.example",   "KE", "Kenya Agricultural and Livestock Research Organization","Station Head","research",["livestock","policy","value-chain"],             "Leading Kenya's research station network on dairy value chains.","0000-0002-1111-0001", "PhD Animal Science",       "phd", 2013, True),
    ("Priya",    "Naidoo",     "priya.naidoo@alumni.example",     "ZA", "University of Pretoria",        "Postdoc",                 "academia",    ["food-systems","nutrition","policy"],            "Postdoc examining urban food security in southern Africa.",      "",                     "PhD Food Science",         "phd", 2023, True),
    ("Rukia",    "Omondi",     "rukia.omondi@alumni.example",     "KE", "GROOTS Kenya",                   "Programme Manager",       "ngo",         ["gender","extension","policy"],                  "Women-in-agriculture advocate working with rural communities.",  "",                     "MSc Gender & Development", "msc", 2020, True),
    ("Samuel",   "Dlamini",    "samuel.dlamini@alumni.example",   "SZ", "Ministry of Agriculture",       "Director of Extension",   "government",  ["extension","policy","agribusiness"],            "Reforming agricultural extension services in Eswatini.",         "",                     "MSc Extension",            "msc", 2017, False),
    ("Tariro",   "Chikwanha",  "tariro.chikwanha@alumni.example", "ZW", "University of Zimbabwe",        "Senior Lecturer",         "academia",    ["agronomy","soil-health","extension"],           "Senior lecturer researching smallholder soil fertility.",        "0000-0002-1111-0002", "PhD Soil Science",         "phd", 2015, True),
    ("Uwamahoro","Claudette",  "claudette.uwa@alumni.example",    "RW", "AGRA Rwanda",                   "Investment Officer",      "ngo",         ["agribusiness","finance","entrepreneurship"],   "Structuring AGRA investments in Rwandan agribusiness.",          "",                     "MSc Agribusiness",         "msc", 2021, True),
    ("Victor",   "Mwesigwa",   "victor.mwesigwa@alumni.example",  "UG", "Tilenga Farm Cooperative",      "Cooperative Lead",        "private",     ["agribusiness","cooperatives","value-chain"],   "Scaling a farmer cooperative across rural Uganda.",              "",                     "MSc Cooperative Management","msc", 2022, True),
    ("Wanjiru",  "Kamau",      "wanjiru.kamau@alumni.example",    "KE", "Strathmore Business School",    "PhD Candidate",           "academia",    ["policy","climate-finance","governance"],        "Researching climate-finance governance for African economies.",  "0000-0002-1111-0003", "PhD Business Admin",       "phd", 2024, True),
    ("Xhosa",    "Ngcobo",     "xhosa.ngcobo@alumni.example",     "ZA", "SA Dept of Agriculture",        "Senior Policy Advisor",   "government",  ["policy","water","food-security"],              "Advisor on water-for-agriculture policy in South Africa.",       "",                     "MSc Agricultural Policy",  "msc", 2019, False),
    ("Yewande",  "Oluwaseun",  "yewande.oluwaseun@alumni.example","NG", "Nestlé West Africa",            "R&D Manager",             "private",     ["food-systems","nutrition","value-chain"],      "Leading R&D for fortified foods across West Africa.",            "",                     "MSc Food Science",         "msc", 2018, True),
]

_GROUPS_SPEC = [
    (
        "Kenya chapter", "country", "KE",
        "RUFORUM alumni based in or working on Kenyan agri-food systems. Quarterly meetups "
        "in Nairobi, policy briefings with the Ministry of Agriculture, and a shared job board.",
    ),
    (
        "Uganda chapter", "country", "UG",
        "Makerere-linked alumni and graduates working across Ugandan agriculture, research, "
        "and extension services. Hosts a monthly journal club and annual regional forum.",
    ),
    (
        "Nigeria chapter", "country", "NG",
        "IITA and NARS-aligned alumni across the 36 states. Focus on cassava, maize, food "
        "systems policy, and agribusiness scale-up.",
    ),
    (
        "Ethiopia chapter", "country", "ET",
        "Alumni at Ethiopian universities, EIAR, and agri-focused ministries. Convenes "
        "Addis-based researchers and diaspora returnees twice a year.",
    ),
    (
        "Southern Africa chapter", "country", None,
        "A trans-national chapter for alumni in SA, Zimbabwe, Zambia, Eswatini and Mozambique. "
        "Shared speakers, cross-border funding announcements, and a mentorship pipeline.",
    ),
    (
        "Agribusiness network", "sector", None,
        "Entrepreneurs, investors and commercial-side researchers building agri ventures. "
        "Includes a private deal-flow channel for seed-stage announcements.",
    ),
    (
        "Agronomy community", "discipline", None,
        "Field scientists, soil specialists, and crop breeders. Monthly reading group on "
        "climate-smart agronomy and a curated ORCID watchlist.",
    ),
    (
        "Gender in Agriculture", "discipline", None,
        "A space for gender-focused researchers, extension agents and policy advocates "
        "advancing women's leadership across African agri-food systems.",
    ),
    (
        "Climate-smart Research", "discipline", None,
        "Climate-resilient cropping, water, and livestock research. Co-hosts the annual "
        "AASW workshop on science-policy translation.",
    ),
    (
        "2020 cohort", "cohort", None,
        "Alumni who graduated from RUFORUM-supported programmes in 2020. Reunion circle, "
        "peer accountability, and a buddy scheme for early-career transitions.",
    ),
    (
        "2022 cohort", "cohort", None,
        "2022 graduates — the most recent full cohort. Job-hunt support, PhD pathway advice, "
        "and a once-a-quarter cohort call.",
    ),
]

_EVENT_SPEC = [
    # (days_from_now, title, location, visibility, description_html)
    (
        7, "Alumni Regional Forum (Nairobi)", "Nairobi", "alumni_only",
        "<p>Our flagship East-African convening — half a day of lightning talks from RUFORUM "
        "alumni, a panel on science-policy translation, and a long-form networking dinner.</p>"
        "<p><strong>Agenda highlights</strong></p>"
        "<ul><li>Keynote: climate-smart agronomy at scale</li>"
        "<li>Fellow flash talks (5 min each)</li>"
        "<li>Open-table mentor circles</li>"
        "<li>Working dinner — optional</li></ul>"
        "<p>Arrive from 16:00 for registration; venue details sent 48h before the event.</p>",
    ),
    (
        14, "Agribusiness Mentorship Clinic", "Virtual", "alumni_only",
        "<p>A focused virtual clinic where alumni entrepreneurs pitch their agribusiness ventures "
        "to a panel of seasoned mentors and receive structured feedback on <em>market fit</em>, "
        "<em>revenue model</em>, and <em>fundraising readiness</em>.</p>"
        "<p>Bring a 2-minute elevator pitch and one question you'd like mentors to weigh in on. "
        "Sessions are recorded with consent and shared with registrants only.</p>",
    ),
    (
        21, "Soil Health Research Symposium", "Kampala", "public",
        "<p>A research-focused symposium spotlighting soil fertility, micronutrient management, "
        "and regenerative practices across East African smallholder systems.</p>"
        "<p>Open to alumni, researchers, and extension partners. Posters welcome — submit "
        "abstracts to <code>events@ruforum.org</code> at least two weeks before.</p>",
    ),
    (
        35, "Climate Finance Policy Roundtable", "Addis Ababa", "alumni_only",
        "<p>Closed-door roundtable with continental policymakers, green-finance institutions, and "
        "RUFORUM alumni working at the research-policy interface. Chatham House rules.</p>"
        "<p>Output: a brief summarising alumni-led recommendations on climate-smart agriculture "
        "financing for the AU agriculture ministers' meeting.</p>",
    ),
    (
        49, "Women in Science Networking Night", "Virtual", "public",
        "<p>A celebration of women RUFORUM alumni shaping African research, policy, and "
        "innovation. Short spotlight talks followed by curated breakout rooms by discipline.</p>"
        "<p>All genders welcome — we're building a broader allyship community.</p>",
    ),
    (
        64, "RUFORUM Alumni Annual General Meeting", "Johannesburg", "alumni_only",
        "<p>The official AGM — governance, chapter updates, and the election of the next alumni "
        "steering committee.</p>"
        "<p><strong>Pre-AGM</strong>: chapter breakouts by country. "
        "<strong>AGM</strong>: reports + voting. "
        "<strong>Post-AGM</strong>: reception and a look ahead to the coming programme year.</p>",
    ),
    (
        90, "Research Methods Masterclass", "Virtual", "alumni_only",
        "<p>A three-part masterclass covering mixed-methods research design, survey instrument "
        "validation, and qualitative coding for agri-food policy work.</p>"
        "<p>Delivered by RUFORUM alumni who now lead methods training at CGIAR centres. "
        "Hands-on — bring a dataset you're working on.</p>",
    ),
    (
        120, "Tech for Agriculture Demo Day", "Lagos", "public",
        "<p>Alumni-led agri-tech startups present working demos to investors, commercial buyers, "
        "and development partners. Categories span precision inputs, digital markets, "
        "post-harvest technologies, and climate analytics.</p>"
        "<p>Demo slots are curated — apply at least six weeks before the event.</p>",
    ),
]

_MILESTONE_SPEC = [
    ("Promoted to Senior Researcher",        "promotion",   730),
    ("Received AWARD African Young Scientist", "award",     180),
    ("Published in Nature Food",              "publication", 90),
    ("Secured $450k research grant",          "grant",       365),
    ("Appointed to national policy task force","leadership", 60),
    ("Delivered keynote at AASW conference",  "speaking",    210),
    ("Filed patent on post-harvest technology","patent",     420),
    ("Recognised as IITA Fellow",             "recognition", 300),
    ("Moved to new position at CGIAR",        "new_job",     150),
    ("Graduated from RUFORUM programme",      "graduation",  1200),
]

_DEGREE_TYPE_MAP = {"phd": "phd", "msc": "msc", "ma": "ma", "bsc": "bsc", "ba": "ba"}


class Command(BaseCommand):
    help = "Seed the alumni module (profiles, tracking, engagement) with a full demo dataset."

    def add_arguments(self, parser):
        parser.add_argument(
            "--from-csv",
            dest="csv_path",
            default=None,
            help="Also run import_alumni_csv against the given file.",
        )
        parser.add_argument(
            "--reset",
            action="store_true",
            help="Delete demo alumni data (by email domain @alumni.example) before reseeding.",
        )
        parser.add_argument(
            "--volume",
            choices=("minimal", "demo", "heavy"),
            default="demo",
            help="Accepted for API symmetry; existing curated cohort is kept.",
        )

    @transaction.atomic
    def handle(self, *args, **options):
        from apps.alumni.engagement.models import (
            AlumniEvent,
            EventRegistration,
            EventVisibility,
            GroupKind,
            GroupMembership,
            MentorshipPairing,
            MentorshipStatus,
            NetworkGroup,
        )
        from apps.alumni.profiles.models import (
            AlumniCertificate,
            AlumniProfile,
            AlumniSource,
            Degree,
            Employment,
            Publication,
        )
        from apps.alumni.profiles.services import create_from_graduation
        from apps.alumni.tracking.models import (
            CareerMilestone,
            ImpactMetric,
            ImpactRecord,
            ImpactSnapshot,
            MilestoneType,
            OrcidSyncLog,
            ResearchOutput,
        )
        from apps.alumni.tracking.services import build_impact_snapshot
        from apps.core.authentication.models import Institution
        from apps.core.permissions.roles import UserRole
        from apps.rims.scholarships.models import GraduationRecord

        User = get_user_model()

        if options.get("reset"):
            removed = AlumniProfile.objects.filter(
                user__email__endswith="@alumni.example"
            ).delete()
            self.stdout.write(
                self.style.WARNING(f"seed_alumni: reset removed {removed[0]} rows")
            )

        random.seed(2026)

        # 1. Promote any seeded RIMS graduations (keeps the handoff wired in demo).
        graduation_created = 0
        for gr in GraduationRecord.objects.select_related(
            "scholar", "scholar__user", "scholar__scholarship"
        ):
            create_from_graduation(gr)
            graduation_created += 1

        # 2. Synthesise direct demo alumni — one per _DEMO_ALUMNI entry.
        synthetic_created = 0
        synthetic_profiles: list[AlumniProfile] = []
        today = timezone.now().date()
        for entry in _DEMO_ALUMNI:
            (
                first,
                last,
                email,
                country,
                employer,
                position,
                sector,
                expertise,
                headline,
                orcid_id,
                programme,
                degree_type_raw,
                grad_year,
                consent,
            ) = entry
            # Country is stored as a full name platform-wide; canonicalise the
            # seed code ("KE") so UserProfile/Employment match directory filters.
            from apps.core.countries import to_country_name

            country = to_country_name(country)

            user, user_created = User.objects.get_or_create(
                email=email,
                defaults={
                    "first_name": first,
                    "last_name": last,
                    "role": UserRole.ALUMNI,
                    "is_active": True,
                },
            )
            if user_created:
                user.set_password("password123")
                user.save(update_fields=["password"])

            # Attach UserProfile country so directory filters work.
            from apps.core.authentication.models import UserProfile

            profile_core, _ = UserProfile.objects.get_or_create(user=user)
            if country and not profile_core.country:
                profile_core.country = country
                profile_core.save(update_fields=["country"])

            institution, _ = Institution.objects.get_or_create(
                name=employer,
                defaults={"country": country, "institution_type": "other"},
            )

            alumni, alumni_created = AlumniProfile.objects.get_or_create(
                user=user,
                defaults={
                    "current_employer": employer,
                    "current_position": position,
                    "current_institution": institution,
                    "orcid_id": orcid_id,
                    "expertise_tags": list(expertise),
                    "headline": headline,
                    "visibility_consent": consent,
                    "published_at": timezone.now() if consent else None,
                    "source": AlumniSource.MANUAL,
                    "source_refs": {"seed": [email]},
                    "linkedin_url": f"https://linkedin.com/in/{first.lower()}-{last.lower()}",
                },
            )
            if alumni_created:
                synthetic_created += 1
            synthetic_profiles.append(alumni)

            # Degree — one per seeded profile.
            Degree.objects.get_or_create(
                profile=alumni,
                programme_name=programme,
                graduated_on=date(grad_year, 6, 30),
                defaults={
                    "institution": institution,
                    "degree_type": _DEGREE_TYPE_MAP.get(degree_type_raw, "other"),
                    "thesis_title": f"{headline[:60]}",
                    "advisor_name": "Prof. RUFORUM Advisor",
                    "funder": "RUFORUM",
                },
            )

            # Employment — one current role.
            Employment.objects.get_or_create(
                profile=alumni,
                organisation=employer,
                position=position,
                defaults={
                    "sector": sector,
                    "country": country,
                    "started_on": date(grad_year + 1, 1, 1),
                    "description": headline,
                },
            )

            # One self-reported publication; 30% get a DOI.
            pub_title = f"{first} {last}: Impact study on {sector}"
            Publication.objects.get_or_create(
                profile=alumni,
                title=pub_title,
                defaults={
                    "authors_text": f"{last}, {first[0]}.",
                    "publication_type": "journal",
                    "doi": f"10.1234/ruforum.{grad_year}.{random.randint(1000, 9999)}"
                    if random.random() < 0.3
                    else "",
                    "year": grad_year + 1,
                },
            )

            # Certificate for some.
            if alumni_created and random.random() < 0.35:
                AlumniCertificate.objects.get_or_create(
                    profile=alumni,
                    title=f"{programme} completion",
                    defaults={
                        "issuer": "RUFORUM",
                        "issued_on": date(grad_year, 7, 15),
                    },
                )

        all_profiles = list(AlumniProfile.objects.select_related("user"))

        # 2b. Opt ~60% of consented alumni into mentoring, give them topics/capacity/statement.
        demo_mentor_topics = [
            ["publishing", "research methods", "grant writing"],
            ["policy engagement", "stakeholder advocacy"],
            ["career transition", "leadership", "team building"],
            ["fundraising strategy", "proposal development"],
            ["climate-smart agriculture", "adaptation policy"],
            ["agribusiness", "go-to-market", "scaling"],
            ["mentoring early-career researchers", "PhD supervision"],
            ["women in science", "STEM leadership", "allyship"],
        ]
        demo_statements = [
            "I coach early-career researchers on publishing strategy and grant hunting — pragmatic, honest feedback.",
            "Happy to unpack policy engagement and how to turn research into briefs that actually land.",
            "Career transitions are my jam — from postdoc to industry, gov, or NGO. Come with a draft CV and three questions.",
            "If you're writing your first big proposal, I'll help you sharpen the theory of change and logframe.",
            "Talk to me about positioning climate-resilient research for AU / AfDB funding streams.",
            "Ex-founder, now investor — I mentor on product-market fit, pitches, and when to raise.",
            "I've supervised 20+ PhDs; happy to share frameworks for structuring a thesis and managing scope.",
            "Allyship and sponsorship for women in agri-science — high-quality referrals and honest reviews.",
        ]
        for idx, profile in enumerate(all_profiles):
            if random.random() < 0.6 and not profile.mentor_accepting:
                profile.mentor_accepting = True
                if not profile.mentor_topics:
                    profile.mentor_topics = list(
                        demo_mentor_topics[idx % len(demo_mentor_topics)]
                    )
                if not profile.mentor_statement:
                    profile.mentor_statement = demo_statements[idx % len(demo_statements)]
                profile.mentor_capacity = random.choice([2, 3, 3, 4, 5])
                profile.save(
                    update_fields=[
                        "mentor_accepting",
                        "mentor_topics",
                        "mentor_statement",
                        "mentor_capacity",
                    ]
                )

        # 3. Network groups.
        groups = {}
        for title, kind_raw, _country, blurb in _GROUPS_SPEC:
            group, _ = NetworkGroup.objects.get_or_create(
                title=title,
                defaults={"kind": kind_raw, "description": blurb},
            )
            # Backfill description for groups seeded before blurbs existed.
            if not group.description or group.description.endswith("— RUFORUM alumni group."):
                group.description = blurb
                group.save(update_fields=["description"])
            groups[title] = group

        # 4. Group memberships — each alumni joins 2-3 groups; country chapters auto-join by country.
        membership_created = 0
        for profile in all_profiles:
            candidate_titles = ["Agronomy community", "Agribusiness network", "Gender in Agriculture", "Climate-smart Research"]
            picks = random.sample(candidate_titles, k=2)
            # Add country chapter if seeded.
            core_profile = getattr(profile.user, "profile", None)
            cc = getattr(core_profile, "country", "") or ""
            country_map = {"KE": "Kenya chapter", "UG": "Uganda chapter", "NG": "Nigeria chapter", "ET": "Ethiopia chapter"}
            if cc in country_map:
                picks.append(country_map[cc])
            elif cc in {"ZA", "ZW", "ZM", "SZ", "MZ"}:
                picks.append("Southern Africa chapter")
            # Cohort group by graduation decade
            earliest = profile.degrees.order_by("graduated_on").first()
            if earliest and earliest.graduated_on.year >= 2022:
                picks.append("2022 cohort")
            elif earliest and earliest.graduated_on.year >= 2020:
                picks.append("2020 cohort")

            for title in set(picks):
                grp = groups.get(title)
                if grp is None:
                    continue
                _, created = GroupMembership.objects.get_or_create(user=profile.user, group=grp)
                if created:
                    membership_created += 1

        # Promote the first two memberships in each group to MODERATOR.
        for grp in NetworkGroup.objects.all():
            moderators_qs = grp.memberships.order_by("joined_at")[:2]
            for m in moderators_qs:
                if m.role != "moderator":
                    m.role = "moderator"
                    m.save(update_fields=["role"])

        # Refresh member_count for each group.
        from django.db.models import Count

        for grp in NetworkGroup.objects.annotate(actual=Count("memberships")):
            if grp.member_count != grp.actual:
                NetworkGroup.objects.filter(pk=grp.pk).update(member_count=grp.actual)

        # 5. Events + registrations.
        event_created = 0
        registration_created = 0
        now = timezone.now()
        vis_map = {
            "public": EventVisibility.PUBLIC,
            "alumni_only": EventVisibility.ALUMNI_ONLY,
            "group": EventVisibility.GROUP,
        }
        for offset_days, title, location, vis, description_html in _EVENT_SPEC:
            event, created = AlumniEvent.objects.get_or_create(
                title=title,
                defaults={
                    "description": description_html,
                    "start_at": now + timedelta(days=offset_days),
                    "end_at": now + timedelta(days=offset_days, hours=3),
                    "location": location,
                    "visibility": vis_map.get(vis, EventVisibility.ALUMNI_ONLY),
                    "capacity": random.choice([0, 0, 50, 80, 120]),
                    "organiser": random.choice(
                        [p.user for p in all_profiles if p.visibility_consent]
                        or [all_profiles[0].user]
                    ),
                },
            )
            if created:
                event_created += 1
            # Backfill rich description / capacity / organiser for events seeded before.
            dirty = []
            if not event.description or event.description.startswith("Seeded"):
                event.description = description_html
                dirty.append("description")
            if event.capacity == 0 and random.random() < 0.6:
                event.capacity = random.choice([50, 80, 120])
                dirty.append("capacity")
            if event.organiser_id is None:
                consented = [p.user for p in all_profiles if p.visibility_consent]
                if consented:
                    event.organiser = random.choice(consented)
                    dirty.append("organiser")
            if dirty:
                event.save(update_fields=dirty)

        # Sweep: any legacy events (earlier seed runs) left with a thin description
        # or no organiser get a generic-but-useful backfill so detail pages aren't bare.
        generic_html = (
            "<p>A RUFORUM alumni gathering — lightning talks, networking, and working "
            "sessions scoped to alumni priorities. Full agenda shared closer to the date.</p>"
            "<p>Arrive a little early for the informal reception; structured programme "
            "starts on the hour.</p>"
        )
        consented_users = [p.user for p in all_profiles if p.visibility_consent]
        for event in AlumniEvent.objects.all():
            dirty = []
            if not event.description or len(event.description) < 60:
                event.description = generic_html
                dirty.append("description")
            if event.organiser_id is None and consented_users:
                event.organiser = random.choice(consented_users)
                dirty.append("organiser")
            if event.capacity == 0 and random.random() < 0.5:
                event.capacity = random.choice([50, 80, 120])
                dirty.append("capacity")
            if dirty:
                event.save(update_fields=dirty)
            # Register random subset (40-70%) of alumni.
            k = random.randint(int(len(all_profiles) * 0.4), int(len(all_profiles) * 0.7))
            for p in random.sample(all_profiles, k=max(1, min(k, len(all_profiles)))):
                _, created_reg = EventRegistration.objects.get_or_create(
                    user=p.user, event=event
                )
                if created_reg:
                    registration_created += 1

        # 6. Career milestones — 1-3 per profile.
        milestone_created = 0
        milestone_type_map = {m.value: m for m in MilestoneType}
        for profile in all_profiles:
            picks = random.sample(_MILESTONE_SPEC, k=random.randint(1, 3))
            for title, mtype_raw, days_ago in picks:
                mtype = milestone_type_map.get(mtype_raw, MilestoneType.OTHER)
                _, created = CareerMilestone.objects.get_or_create(
                    profile=profile,
                    milestone_type=mtype,
                    title=title,
                    defaults={
                        "description": f"Automatically seeded — {title}.",
                        "occurred_on": today - timedelta(days=days_ago),
                    },
                )
                if created:
                    milestone_created += 1

        # 7. Research outputs for profiles with ORCID iDs.
        research_created = 0
        for profile in all_profiles:
            if not profile.orcid_id:
                continue
            for i in range(random.randint(2, 5)):
                year = random.randint(2019, 2024)
                put_code = f"seed-{profile.pk}-{i}"
                _, created = ResearchOutput.objects.get_or_create(
                    profile=profile,
                    put_code=put_code,
                    defaults={
                        "title": f"Research output {i + 1} by {profile.user.last_name}",
                        "publication_type": "journal-article",
                        "doi": f"10.9999/seed.{profile.pk}.{i}",
                        "year": year,
                        "payload": {"source": "seed"},
                    },
                )
                if created:
                    research_created += 1
            OrcidSyncLog.objects.get_or_create(
                profile=profile,
                defaults={
                    "orcid_id": profile.orcid_id,
                    "last_success_at": timezone.now(),
                    "works_imported_count": random.randint(3, 10),
                },
            )

        # 8. Impact records spanning 3 quarterly periods.
        impact_created = 0
        metrics_pool = [
            (ImpactMetric.CITATIONS, lambda: Decimal(random.randint(10, 350))),
            (ImpactMetric.FUNDING_SECURED_USD, lambda: Decimal(random.randint(5000, 500000))),
            (ImpactMetric.STUDENTS_SUPERVISED, lambda: Decimal(random.randint(1, 25))),
            (ImpactMetric.JOBS_CREATED, lambda: Decimal(random.randint(0, 40))),
            (ImpactMetric.POLICIES_INFLUENCED, lambda: Decimal(random.randint(0, 5))),
            (ImpactMetric.MEDIA_MENTIONS, lambda: Decimal(random.randint(0, 30))),
        ]
        current_year = today.year
        periods = [
            f"{current_year - 1}",
            f"{current_year}",
            f"{current_year}-Q1",
        ]
        for profile in all_profiles:
            # Each profile gets 2-4 metrics per period for 1-3 periods.
            chosen_periods = random.sample(periods, k=random.randint(1, 3))
            for period in chosen_periods:
                for metric, value_fn in random.sample(
                    metrics_pool, k=random.randint(2, 4)
                ):
                    _, created = ImpactRecord.objects.get_or_create(
                        profile=profile,
                        metric=metric,
                        period_label=period,
                        defaults={"value": value_fn(), "source": "self"},
                    )
                    if created:
                        impact_created += 1

        # 9. Mentorship pairings — pair mentors (alumni) with mentees (other alumni or scholars).
        pairing_created = 0
        mentor_pool = [p for p in all_profiles if p.visibility_consent][:12]
        mentee_pool = [p for p in all_profiles if p not in mentor_pool][:20]
        # Fallback: if too few distinct, rotate.
        if len(mentor_pool) >= 2 and len(mentee_pool) >= 2:
            for i in range(min(12, len(mentor_pool))):
                mentor = mentor_pool[i]
                mentee = mentee_pool[i % len(mentee_pool)]
                if mentor.user_id == mentee.user_id:
                    continue
                status_pick = random.choice(
                    [
                        MentorshipStatus.REQUESTED,
                        MentorshipStatus.ACTIVE,
                        MentorshipStatus.ACTIVE,
                        MentorshipStatus.COMPLETED,
                    ]
                )
                started = (
                    timezone.now() - timedelta(days=random.randint(30, 365))
                    if status_pick
                    in {MentorshipStatus.ACTIVE, MentorshipStatus.COMPLETED}
                    else None
                )
                ended = (
                    timezone.now() - timedelta(days=random.randint(1, 30))
                    if status_pick == MentorshipStatus.COMPLETED
                    else None
                )
                pairing, created = MentorshipPairing.objects.get_or_create(
                    mentor=mentor,
                    mentee=mentee.user,
                    topic=random.choice(
                        [
                            "Research career planning",
                            "Policy engagement",
                            "Funding strategy",
                            "Publication in high-impact journals",
                            "Leadership transition",
                        ]
                    ),
                    defaults={
                        "status": status_pick,
                        "match_score": round(random.uniform(0.55, 0.95), 2),
                        "started_at": started,
                        "ended_at": ended,
                        "goals": "<p>Sharpen publication pipeline, identify the right funders, and build a peer network for accountability.</p>",
                        "cadence": random.choice(["monthly", "biweekly", "as_needed"]),
                        "duration_months": random.choice([3, 6, 9, 12]),
                        "outcome": (
                            "<p>Completed a shared publication plan; mentee submitted a Nature-tier paper and secured a $45k seed grant.</p>"
                            if status_pick == MentorshipStatus.COMPLETED
                            else ""
                        ),
                        "outcome_rating": (
                            random.choice([3, 4, 4, 5])
                            if status_pick == MentorshipStatus.COMPLETED
                            else None
                        ),
                    },
                )
                if created:
                    pairing_created += 1
                    # Seed a realistic session journal on active / completed pairings.
                    if status_pick in {MentorshipStatus.ACTIVE, MentorshipStatus.COMPLETED}:
                        from apps.alumni.engagement.models import MentorshipNote
                        sample_notes = [
                            "<p>Kickoff call — scoped goals, agreed biweekly cadence, and shared reading list.</p>",
                            "<p>Discussed a draft manuscript outline and target journals. Action: mentee refines intro by next session.</p>",
                            "<p>Reviewed funding shortlist — mentee to draft a concept note for the AU Research Grants call.</p>",
                        ]
                        for i, content in enumerate(sample_notes[: random.randint(1, 3)]):
                            MentorshipNote.objects.create(
                                pairing=pairing,
                                author=mentor.user if i % 2 == 0 else mentee.user,
                                content=content,
                            )

        # 10. Impact snapshots — seed the last four quarters for a trend line.
        snapshot_created = 0
        seen_labels: set[str] = set()
        for quarters_back in range(4):
            # Step back in ~90-day chunks to land in a different quarter each time.
            anchor = today.replace(day=1) - timedelta(days=90 * quarters_back)
            quarter = ((anchor.month - 1) // 3) + 1
            label = f"{anchor.year}-Q{quarter}"
            if label in seen_labels:
                continue
            seen_labels.add(label)
            snapshot = build_impact_snapshot(label)
            if snapshot:
                snapshot_created += 1

        self.stdout.write(
            self.style.SUCCESS(
                "seed_alumni complete:\n"
                f"  - profiles from graduations: {graduation_created}\n"
                f"  - synthetic profiles created: {synthetic_created}\n"
                f"  - total alumni profiles:      {AlumniProfile.objects.count()}\n"
                f"  - public (consented):         {AlumniProfile.objects.filter(visibility_consent=True).count()}\n"
                f"  - network groups:             {NetworkGroup.objects.count()}\n"
                f"  - group memberships created:  {membership_created}\n"
                f"  - events created:             {event_created}\n"
                f"  - event registrations:        {registration_created}\n"
                f"  - milestones created:         {milestone_created}\n"
                f"  - research outputs (ORCID):   {research_created}\n"
                f"  - impact records:             {impact_created}\n"
                f"  - mentorship pairings:        {pairing_created}\n"
                f"  - impact snapshots (touched): {snapshot_created}\n"
            )
        )

        csv_path = options.get("csv_path")
        if csv_path:
            from django.core.management import call_command

            call_command("import_alumni_csv", file=csv_path)
