"""Seed a synthetic user pool covering every UserRole.

Two outputs:

1. **10 named personas** with the password ``password123`` — these are the
   "test users" the demo verification flow logs in as:

       student.demo@iilmp.local
       scholar.demo@iilmp.local
       instructor.demo@iilmp.local
       reviewer.demo@iilmp.local
       finance.demo@iilmp.local
       auditor.demo@iilmp.local
       mel.demo@iilmp.local
       entrepreneur.demo@iilmp.local
       mentor.demo@iilmp.local
       alumni.demo@iilmp.local

2. **N synthetic users** with emails of the form ``user.seed.<n>@iilmp.local``,
   spread across every UserRole. Default counts come from
   ``apps.core.seeders.volumes.RECORD_COUNTS``.

Every synthetic user gets a populated ``UserProfile`` (country, gender,
date_of_birth, bio, areas_of_interest), a default ``ProfileVisibility`` row,
and an Institution attached.

Idempotent: re-running fills in any missing rows. ``--reset`` wipes only the
synthetic rows (``user.seed.*``) — the named personas stay.
"""

from __future__ import annotations

import random
from datetime import date, timedelta

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

from apps.core.authentication.models import Institution, ProfileVisibility, UserProfile
from apps.core.countries import to_country_name
from apps.core.permissions.roles import UserRole
from apps.core.seeders.volumes import RECORD_COUNTS

User = get_user_model()

DEFAULT_PASSWORD = "password123"

# (email, first, last, role, country, bio_hint)
NAMED_PERSONAS = [
    ("student.demo@iilmp.local",      "Sarah",   "Student",       UserRole.LEARNER,         "UG", "First-year learner exploring the catalogue."),
    ("scholar.demo@iilmp.local",      "Samuel",  "Scholar",       UserRole.SCHOLAR,         "KE", "RUFORUM-funded MSc scholar in agricultural economics."),
    ("instructor.demo@iilmp.local",   "Ines",    "Instructor",    UserRole.INSTRUCTOR,      "TZ", "Lecturer at Sokoine University; runs the soil-health course."),
    ("reviewer.demo@iilmp.local",     "Rita",    "Reviewer",      UserRole.REVIEWER,        "ZA", "Independent grant reviewer specialising in climate research."),
    ("finance.demo@iilmp.local",      "Felix",   "Finance",       UserRole.FINANCE_OFFICER, "RW", "Finance officer responsible for grant disbursements."),
    ("auditor.demo@iilmp.local",      "Akua",    "Auditor",       UserRole.AUDITOR,         "GH", "Internal auditor with read-only access to all financial records."),
    ("mel.demo@iilmp.local",          "Moses",   "MEL",           UserRole.MEL_OFFICER,     "ET", "M&E officer overseeing programme indicators."),
    ("entrepreneur.demo@iilmp.local", "Eunice",  "Entrepreneur",  UserRole.ENTREPRENEUR,    "UG", "Founder of an agritech startup in the SME Hub pipeline."),
    ("mentor.demo@iilmp.local",       "Maria",   "Mentor",        UserRole.MENTOR,          "SN", "Senior researcher mentoring entrepreneurs and scholars."),
    ("alumni.demo@iilmp.local",       "Adam",    "Alumnus",       UserRole.ALUMNI,          "ZA", "Long-time alumnus, now leading a research consortium."),
]

# Approximate role distribution at heavy volume (heavy total = 400).
# Each entry maps a role to its share of the synthetic pool.
ROLE_WEIGHTS = [
    (UserRole.LEARNER,           0.28),
    (UserRole.SCHOLAR,           0.12),
    (UserRole.ALUMNI,            0.10),
    (UserRole.INSTRUCTOR,        0.05),
    (UserRole.REP_LEARNING_ADMIN, 0.02),
    (UserRole.REVIEWER,          0.05),
    (UserRole.RESEARCHER,        0.05),
    (UserRole.GRANTS_MANAGER,    0.02),
    (UserRole.PROGRAM_MANAGER,   0.02),
    (UserRole.PROGRAM_DIRECTOR,  0.01),
    (UserRole.PROGRAMME_OFFICER, 0.02),
    (UserRole.FINANCE_OFFICER,   0.02),
    (UserRole.FINANCE_MANAGER,   0.02),
    (UserRole.FINANCE_DIRECTOR,  0.01),
    (UserRole.PROJECT_MANAGER,   0.02),
    (UserRole.PROGRAM_COORDINATOR, 0.01),
    (UserRole.ENTREPRENEUR,      0.05),
    (UserRole.MENTOR,            0.03),
    (UserRole.INVESTOR,          0.02),
    (UserRole.MEL_OFFICER,       0.02),
    (UserRole.REPO_MANAGER,      0.02),
    (UserRole.ALUMNI_OFFICER,    0.01),
    (UserRole.AUDITOR,           0.01),
    (UserRole.SME_ADMIN,         0.01),
    (UserRole.SERVICE_PROVIDER,  0.005),
    (UserRole.PARTNER,           0.005),
    (UserRole.BUYER,             0.005),
    (UserRole.FUNDER,            0.005),
    (UserRole.JUDGE,             0.005),
    (UserRole.APPLICANT,         0.01),
]

INSTITUTIONS = [
    ("Makerere University",                          "UG", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Nairobi",                        "KE", Institution.InstitutionType.UNIVERSITY,  True),
    ("Sokoine University of Agriculture",            "TZ", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Pretoria",                       "ZA", Institution.InstitutionType.UNIVERSITY,  True),
    ("Addis Ababa University",                       "ET", Institution.InstitutionType.UNIVERSITY,  True),
    ("Ahmadu Bello University",                      "NG", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Ghana",                          "GH", Institution.InstitutionType.UNIVERSITY,  True),
    ("Cheikh Anta Diop University",                  "SN", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Zambia",                         "ZM", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Zimbabwe",                       "ZW", Institution.InstitutionType.UNIVERSITY,  True),
    ("National University of Rwanda",                "RW", Institution.InstitutionType.UNIVERSITY,  True),
    ("Eduardo Mondlane University",                  "MZ", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Cape Coast",                     "GH", Institution.InstitutionType.UNIVERSITY,  True),
    ("University of Malawi",                         "MW", Institution.InstitutionType.UNIVERSITY,  True),
    ("Strathmore University",                        "KE", Institution.InstitutionType.UNIVERSITY,  True),
    ("Egerton University",                           "KE", Institution.InstitutionType.UNIVERSITY,  True),
    ("Bunda College of Agriculture",                 "MW", Institution.InstitutionType.UNIVERSITY,  True),
    ("Stellenbosch University",                      "ZA", Institution.InstitutionType.UNIVERSITY,  True),
    ("Kenyatta University",                          "KE", Institution.InstitutionType.UNIVERSITY,  True),
    ("Mekelle University",                           "ET", Institution.InstitutionType.UNIVERSITY,  True),
    ("Africa Rice Center (AfricaRice)",              "CI", Institution.InstitutionType.RESEARCH,    False),
    ("International Livestock Research Institute",   "KE", Institution.InstitutionType.RESEARCH,    False),
    ("International Institute of Tropical Agriculture", "NG", Institution.InstitutionType.RESEARCH, False),
    ("CGIAR — World Agroforestry",                   "KE", Institution.InstitutionType.RESEARCH,    False),
    ("Kenya Agricultural and Livestock Research Organization", "KE", Institution.InstitutionType.RESEARCH, False),
    ("National Agricultural Research Organisation",  "UG", Institution.InstitutionType.RESEARCH,    False),
    ("ISRA — Senegalese Institute of Agricultural Research", "SN", Institution.InstitutionType.RESEARCH, False),
    ("Ethiopian Institute of Agricultural Research", "ET", Institution.InstitutionType.RESEARCH,    False),
    ("Zambia Agriculture Research Institute",        "ZM", Institution.InstitutionType.RESEARCH,    False),
    ("Council for Scientific and Industrial Research — Ghana", "GH", Institution.InstitutionType.RESEARCH, False),
]

INTERESTS = [
    "soil-health", "climate-resilience", "agronomy", "plant-breeding", "agribusiness",
    "value-chain", "policy", "gender", "youth-engagement", "nutrition", "livestock",
    "post-harvest", "remote-sensing", "data-science", "extension", "irrigation",
    "agroforestry", "food-systems", "entrepreneurship", "digital-ag", "drought-tolerance",
    "smallholder-finance", "carbon-markets", "crop-science", "cooperatives",
]


def _ensure_institutions(target_count: int) -> list[Institution]:
    """Create up to ``target_count`` institutions from INSTITUTIONS pool."""
    objs = []
    for name, country, itype, is_member in INSTITUTIONS[:target_count]:
        inst, _ = Institution.objects.get_or_create(
            name=name,
            defaults={
                "country": to_country_name(country),
                "institution_type": itype,
                "is_member": is_member,
                "is_active": True,
                "region": "Africa",
            },
        )
        objs.append(inst)
    return objs


def _ensure_persona(email: str, first: str, last: str, role: str,
                    country: str, bio: str, institution: Institution) -> User:
    user, created = User.objects.get_or_create(
        email=email,
        defaults={
            "first_name": first,
            "last_name": last,
            "role": role,
            "is_active": True,
            "institution": institution,
            "email_verified": True,
        },
    )
    if created:
        user.set_password(DEFAULT_PASSWORD)
        user.save()
    else:
        # Keep credentials in sync so the verification flow always works.
        if not user.check_password(DEFAULT_PASSWORD):
            user.set_password(DEFAULT_PASSWORD)
            user.save()

    profile, _ = UserProfile.objects.get_or_create(
        user=user,
        defaults={
            "country": to_country_name(country),
            "bio": bio,
            "gender": UserProfile.Gender.PREFER_NOT_TO_SAY,
            "degree_level": UserProfile.DegreeLevel.MASTERS,
            "areas_of_interest": ",".join(random.sample(INTERESTS, k=4)),
        },
    )
    ProfileVisibility.objects.get_or_create(user=user)
    return user


def _make_synthetic(
    *,
    index: int,
    role: str,
    institutions: list[Institution],
    fake,
) -> tuple[User, bool]:
    """Create one synthetic user with a profile. Returns (user, created)."""
    email = f"user.seed.{index:04d}@iilmp.local"
    first = fake.first_name()
    last = fake.last_name()
    institution = random.choice(institutions)

    user, created = User.objects.get_or_create(
        email=email,
        defaults={
            "first_name": first,
            "last_name": last,
            "role": role,
            "is_active": True,
            "institution": institution,
            "email_verified": True,
        },
    )
    if created:
        user.set_password(DEFAULT_PASSWORD)
        user.save()

    age = random.randint(22, 60)
    dob = date.today() - timedelta(days=age * 365 + random.randint(0, 364))
    UserProfile.objects.update_or_create(
        user=user,
        defaults={
            "country": institution.country or to_country_name(random.choice(["UG", "KE", "TZ", "ZA", "GH", "NG", "ET", "RW", "MW", "ZM"])),
            "gender": random.choice([
                UserProfile.Gender.FEMALE,
                UserProfile.Gender.MALE,
                UserProfile.Gender.PREFER_NOT_TO_SAY,
            ]),
            "date_of_birth": dob,
            "degree_level": random.choice([
                UserProfile.DegreeLevel.UNDERGRADUATE,
                UserProfile.DegreeLevel.MASTERS,
                UserProfile.DegreeLevel.PHD,
            ]),
            "bio": fake.paragraph(nb_sentences=2),
            "professional_background": fake.paragraph(nb_sentences=3),
            "areas_of_interest": ",".join(random.sample(INTERESTS, k=random.randint(2, 5))),
        },
    )
    ProfileVisibility.objects.get_or_create(user=user)
    return user, created


def _distribute_roles(total: int) -> list[str]:
    """Return a list of length ``total`` with role values per ROLE_WEIGHTS."""
    out = []
    for role, weight in ROLE_WEIGHTS:
        n = max(1, round(total * weight))
        out.extend([role] * n)
    if len(out) > total:
        out = out[:total]
    while len(out) < total:
        out.append(random.choice([r for r, _ in ROLE_WEIGHTS]))
    random.shuffle(out)
    return out


class Command(BaseCommand):
    help = "Seed a synthetic user pool (Faker) plus 10 named demo personas."

    def add_arguments(self, parser):
        parser.add_argument(
            "--volume",
            choices=("minimal", "demo", "heavy"),
            default="demo",
        )
        parser.add_argument(
            "--reset",
            action="store_true",
            help="Delete synthetic seed users (user.seed.*) before seeding.",
        )
        parser.add_argument(
            "--count",
            type=int,
            default=None,
            help="Override the synthetic user count from the volume tier.",
        )

    @transaction.atomic
    def handle(self, *args, **opts):
        from faker import Faker  # imported lazily so the seeder is optional at install time

        tier = opts["volume"]
        counts = RECORD_COUNTS[tier]
        target = opts["count"] or counts["users"]

        fake = Faker()
        Faker.seed(2026)
        random.seed(2026)

        if opts["reset"]:
            qs = User.objects.filter(email__istartswith="user.seed.")
            removed = qs.count()
            qs.delete()
            self.stdout.write(self.style.WARNING(f"Removed {removed} synthetic users."))

        # Institutions.
        institutions = _ensure_institutions(counts["institutions"])
        self.stdout.write(self.style.SUCCESS(f"Institutions: {len(institutions)}"))

        # Named personas.
        default_inst = institutions[0]
        for email, first, last, role, country, bio in NAMED_PERSONAS:
            inst = next(
                (i for i in institutions if i.country == to_country_name(country)),
                default_inst,
            )
            _ensure_persona(email, first, last, role, country, bio, inst)
        self.stdout.write(self.style.SUCCESS(f"Personas ensured: {len(NAMED_PERSONAS)} (password=password123)"))

        # Synthetic pool.
        created = 0
        existing = 0
        roles = _distribute_roles(target)
        for i, role in enumerate(roles, start=1):
            _, was_created = _make_synthetic(
                index=i,
                role=role,
                institutions=institutions,
                fake=fake,
            )
            if was_created:
                created += 1
            else:
                existing += 1

        self.stdout.write(self.style.SUCCESS(
            f"Synthetic users: created={created} existing={existing} "
            f"(target={target}, tier={tier})"
        ))
        total = User.objects.count()
        self.stdout.write(self.style.SUCCESS(f"Total users in DB: {total}"))
