"""Seed SME-Hub investment (SP5) demo data.

Idempotent. Builds on SP1 (verified entrepreneurs + businesses), SP3, and
SP4 (so DealRooms get reused on accept).

Produces approximately:
  • 25 Investors (mixed types and statuses)
  • 10 FundingCalls (mix of types and statuses)
  • 30 FundingApplications across draft / submitted / under_review / accepted / rejected
  • Investor matches refreshed for every verified entrepreneur
  • 15 InvestorConnectionRequests (mixed acceptance + opens DealRooms)
  • 5 ManualFundingRecords
"""
from __future__ import annotations

import logging
import random
from datetime import timedelta
from decimal import Decimal

from django.contrib.auth import get_user_model
from django.contrib.auth.hashers import make_password
from django.db import transaction
from django.utils import timezone

from apps.core.countries import to_country_name
from apps.core.permissions.roles import UserRole
from apps.smehub.investment.models import (
    FundingApplication,
    FundingCall,
    Investor,
    InvestorConnectionRequest,
    ManualFundingRecord,
)
from apps.smehub.investment.services import (
    accept_investor_request,
    compile_performance_snapshot,
    compute_readiness,
    refresh_matches_for,
)
from apps.smehub.onboarding.models import Business, EntrepreneurProfile

logger = logging.getLogger(__name__)
User = get_user_model()

DEMO_PASSWORD = "smehub-demo-2026"  # noqa: S105 — demo only

INVESTORS_SEED = [
    ("Savannah Seed Capital", Investor.Type.ANGEL, ["agriculture", "agritech"], ["KE", "UG"], 5_000, 50_000),
    ("Hekima Ventures", Investor.Type.VC, ["agritech", "fintech"], ["KE", "TZ", "RW"], 50_000, 500_000),
    ("AfDB SME Vehicle", Investor.Type.DFI, ["agriculture", "manufacturing"], ["KE", "UG", "TZ", "RW"], 250_000, 2_000_000),
    ("Cassava Pilot Foundation", Investor.Type.FOUNDATION, ["agriculture"], ["UG"], 10_000, 75_000),
    ("Gates Agriculture Programme", Investor.Type.DONOR, ["agriculture", "education"], ["KE", "UG", "ET"], 75_000, 500_000),
    ("Mwangaza Angel Network", Investor.Type.ANGEL, ["energy", "fintech"], ["KE"], 5_000, 30_000),
    ("EnAble VC", Investor.Type.VC, ["energy", "manufacturing"], ["KE", "ZA"], 100_000, 1_000_000),
    ("MasterCard Foundation Catalyst", Investor.Type.DONOR, ["education", "agriculture", "fintech"], ["KE", "UG", "RW", "TZ"], 50_000, 250_000),
    ("Uganda Innovation Fund", Investor.Type.GOV_PROGRAMME, ["education", "manufacturing"], ["UG"], 5_000, 100_000),
    ("Tanzania Industrial Catalyst", Investor.Type.GOV_PROGRAMME, ["manufacturing", "energy"], ["TZ"], 25_000, 500_000),
    ("Cogito Climate Fund", Investor.Type.VC, ["agriculture", "energy"], ["KE", "RW"], 100_000, 750_000),
    ("Mountains of the Moon Capital", Investor.Type.ANGEL, ["agritech", "manufacturing"], ["UG"], 5_000, 75_000),
    ("Silverback Foundation", Investor.Type.FOUNDATION, ["healthcare", "agriculture"], ["RW", "UG"], 25_000, 100_000),
    ("Zanzibar Blue Capital", Investor.Type.VC, ["agriculture", "fintech"], ["TZ"], 75_000, 600_000),
    ("Pan-African DFI Window", Investor.Type.DFI, ["manufacturing", "energy", "agriculture"], ["KE", "UG", "TZ", "ZA", "RW"], 500_000, 5_000_000),
    ("Mboga Ventures", Investor.Type.VC, ["agriculture"], ["KE"], 25_000, 250_000),
    ("Acumen East Africa", Investor.Type.VC, ["healthcare", "education"], ["KE", "UG"], 100_000, 1_000_000),
    ("Tubaki Family Office", Investor.Type.ANGEL, ["fintech"], ["TZ"], 10_000, 100_000),
    ("Heifer International Fund", Investor.Type.DONOR, ["agriculture"], ["KE", "UG", "TZ"], 25_000, 200_000),
    ("Kenya Climate Innovation", Investor.Type.GOV_PROGRAMME, ["agriculture", "energy"], ["KE"], 5_000, 50_000),
    ("Rwanda Bio Pioneers", Investor.Type.ANGEL, ["healthcare", "agritech"], ["RW"], 5_000, 50_000),
    ("Equity Catalyst Fund", Investor.Type.VC, ["fintech", "education"], ["KE", "UG"], 100_000, 750_000),
    ("Sasakawa Africa Programme", Investor.Type.DONOR, ["agriculture"], ["KE", "UG", "TZ", "ET"], 30_000, 150_000),
    ("Kampala Foundation Trust", Investor.Type.FOUNDATION, ["education"], ["UG"], 5_000, 50_000),
    ("Pending Ventures (PV)", Investor.Type.VC, ["fintech"], ["KE"], 50_000, 300_000),
]

FUNDING_CALLS_SEED = [
    ("Smallholder Resilience Seed Round", FundingCall.Type.SEED, ["agriculture"], ["idea", "mvp", "early_revenue"], ["KE", "UG"], 5_000, 50_000),
    ("Agritech Proof-of-Concept Grant", FundingCall.Type.POC_GRANT, ["agritech"], ["mvp"], ["KE", "UG", "TZ"], 10_000, 30_000),
    ("Renewable Energy Growth Round", FundingCall.Type.GROWTH, ["energy"], ["growth", "scale"], ["KE", "RW"], 100_000, 750_000),
    ("Women in Agribusiness Challenge", FundingCall.Type.CHALLENGE_FUND, ["agriculture", "agritech"], ["mvp", "early_revenue"], ["KE", "UG", "RW"], 5_000, 25_000),
    ("Manufacturing Scale-up Investment", FundingCall.Type.GROWTH, ["manufacturing"], ["growth", "scale"], ["TZ", "KE"], 250_000, 2_000_000),
    ("Climate Innovation Seed", FundingCall.Type.SEED, ["agriculture", "energy"], ["idea", "mvp"], ["KE", "UG", "TZ"], 5_000, 25_000),
    ("Health Tech POC Grant", FundingCall.Type.POC_GRANT, ["healthcare"], ["mvp"], ["KE", "UG"], 10_000, 50_000),
    ("Pan-African Growth Fund", FundingCall.Type.GROWTH, ["fintech", "agritech"], ["growth"], ["KE", "UG", "TZ", "RW", "ZA"], 200_000, 1_500_000),
    ("Rural Education Challenge", FundingCall.Type.CHALLENGE_FUND, ["education"], ["mvp", "early_revenue"], ["UG", "RW"], 5_000, 30_000),
    ("Smallholder Tech Seed (Closed)", FundingCall.Type.SEED, ["agriculture", "agritech"], ["idea", "mvp"], ["KE", "UG"], 5_000, 25_000),
]


def _ensure_investor_user(idx: int) -> User:
    email = f"investor{idx:02d}@example.com"
    user, created = User.objects.get_or_create(
        email=email,
        defaults={
            "first_name": f"Inv{idx:02d}",
            "last_name": "Manager",
            "is_active": True,
            "email_verified": True,
            "role": UserRole.INVESTOR,
        },
    )
    if created:
        user.password = make_password(DEMO_PASSWORD)
        user.save(update_fields=["password"])
    return user


def _ensure_admin() -> User:
    user, created = User.objects.get_or_create(
        email="smehub-investment-admin@ruforum.org",
        defaults={
            "first_name": "Imani",
            "last_name": "Investment",
            "is_active": True,
            "email_verified": True,
            "role": UserRole.SME_ADMIN,
        },
    )
    if created:
        user.password = make_password(DEMO_PASSWORD)
        user.save(update_fields=["password"])
    return user


def _seed_investors(admin: User) -> list[Investor]:
    investors: list[Investor] = []
    n = len(INVESTORS_SEED)
    for idx, (name, kind, focus, geo, lo, hi) in enumerate(INVESTORS_SEED, start=1):
        contact_user = _ensure_investor_user(idx)
        investor, created = Investor.objects.get_or_create(
            org_name=name,
            defaults={
                "type": kind,
                "investment_focus": focus,
                "geographic_preference": [to_country_name(c) for c in geo],
                "ticket_size_min": Decimal(lo),
                "ticket_size_max": Decimal(hi),
                "contact_email": contact_user.email,
                "contact_phone": f"+254700{700 + idx:03d}",
                "website": f"https://example.com/inv{idx:02d}",
                "description": f"{name} backs SMEs in {', '.join(focus)} across {', '.join(geo)}.",
                "contact_user": contact_user,
            },
        )
        if created:
            if idx <= n - 4:
                investor.verify(by_user=admin)
                investor.save()
            elif idx == n - 1:
                investor.deactivate(reason="Seasonal pause")
                investor.save()
            elif idx == n - 2:
                investor.reject(reason="Insufficient compliance documents.")
                investor.save()
            # The remaining one stays PENDING
        investors.append(investor)
    return investors


def _seed_funding_calls(admin: User, investors: list[Investor]) -> list[FundingCall]:
    calls: list[FundingCall] = []
    now = timezone.now()
    verified_pool = [i for i in investors if i.verification_status == Investor.Status.VERIFIED]
    for idx, (title, kind, sectors, stages, countries, lo, hi) in enumerate(FUNDING_CALLS_SEED, start=1):
        deadline = now + timedelta(days=30 + idx * 5)
        call, created = FundingCall.objects.get_or_create(
            title=title,
            defaults={
                "type": kind,
                "summary": f"Apply to the {title}.",
                "description": f"Detailed description of {title} — eligibility, evaluation criteria, and timeline.",
                "target_sectors": sectors,
                "target_stages": stages,
                "target_countries": [to_country_name(c) for c in countries],
                "amount_min": Decimal(lo),
                "amount_max": Decimal(hi),
                "application_deadline": deadline,
                "contact_email": "smehub-funding@ruforum.org",
                "organiser": admin,
                "investor": verified_pool[idx % len(verified_pool)] if verified_pool else None,
            },
        )
        if created:
            if idx == len(FUNDING_CALLS_SEED):
                call.publish()
                call.save()
                call = FundingCall.objects.get(pk=call.pk)
                call.close()
                call.save()
            elif idx >= 2:
                call.publish()
                call.save()
        calls.append(call)
    return calls


def _seed_applications(calls: list[FundingCall], admin: User) -> dict:
    submitted = 0
    accepted = 0
    rejected = 0
    drafts = 0
    returned = 0
    published_calls = [c for c in calls if c.status == FundingCall.Status.PUBLISHED]
    if not published_calls:
        return {"applications": FundingApplication.objects.count()}
    verified_businesses = list(
        Business.objects.filter(
            verification_status=Business.Status.VERIFIED,
        ).select_related("entrepreneur")[:30]
    )
    if not verified_businesses:
        return {"applications": FundingApplication.objects.count()}
    target = 30
    pairs = []
    used = set()
    attempts = 0
    while len(pairs) < target and attempts < target * 8:
        attempts += 1
        c = random.choice(published_calls)
        b = random.choice(verified_businesses)
        if (c.pk, b.pk) in used:
            continue
        used.add((c.pk, b.pk))
        pairs.append((c, b))
    for idx, (call, business) in enumerate(pairs):
        application, created = FundingApplication.objects.get_or_create(
            funding_call=call,
            business=business,
            defaults={
                "entrepreneur": business.entrepreneur,
                "funding_request_amount": Decimal(
                    random.randint(int(call.amount_min or 5000), int(call.amount_max or 50000)),
                ),
                "pitch_summary": f"Pitch for {business.name} responding to {call.title[:60]}",
                "use_of_funds": "Working capital, equipment, and team expansion.",
                "traction": "Pilot customers and revenue runway demonstrated.",
                "team_summary": "Founders + 3 specialists with sector expertise.",
            },
        )
        if not created:
            continue
        bucket = idx % 6
        if bucket == 0:
            drafts += 1
        elif bucket == 1:
            application.submit()
            application.save()
            submitted += 1
        elif bucket == 2:
            application.submit()
            application.save()
            application = FundingApplication.objects.get(pk=application.pk)
            application.begin_review()
            application.save()
            submitted += 1
        elif bucket == 3:
            application.submit()
            application.save()
            application = FundingApplication.objects.get(pk=application.pk)
            application.accept(by_user=admin, note="Strong fit with our criteria.")
            application.save()
            accepted += 1
        elif bucket == 4:
            application.submit()
            application.save()
            application = FundingApplication.objects.get(pk=application.pk)
            application.reject(by_user=admin, note="Not a fit this round.")
            application.save()
            rejected += 1
        else:
            application.submit()
            application.save()
            application = FundingApplication.objects.get(pk=application.pk)
            application.return_for_info(message="Please attach updated financials.")
            application.save()
            returned += 1
    return {
        "applications": FundingApplication.objects.count(),
        "drafts": drafts,
        "submitted": submitted,
        "accepted": accepted,
        "rejected": rejected,
        "returned_for_info": returned,
    }


def _seed_matches() -> int:
    refreshed = 0
    for ent in EntrepreneurProfile.objects.filter(
        verification_status=EntrepreneurProfile.Status.VERIFIED,
    ).iterator():
        try:
            compile_performance_snapshot(ent, trigger="seed")
            compute_readiness(ent)
            refresh_matches_for(ent, trigger="seed")
            refreshed += 1
        except Exception as exc:  # noqa: BLE001
            logger.warning("seed match refresh failed: %s", exc)
    return refreshed


def _seed_connection_requests(investors: list[Investor], admin: User) -> dict:
    verified_invs = [i for i in investors if i.verification_status == Investor.Status.VERIFIED]
    verified_businesses = list(
        Business.objects.filter(
            verification_status=Business.Status.VERIFIED,
        ).select_related("entrepreneur")[:15]
    )
    if not verified_invs or not verified_businesses:
        return {"requests": 0, "accepted": 0, "declined": 0}
    pending = 0
    accepted = 0
    declined = 0
    target = min(15, len(verified_businesses) * len(verified_invs))
    pairs = []
    seen = set()
    attempts = 0
    while len(pairs) < target and attempts < target * 8:
        attempts += 1
        inv = random.choice(verified_invs)
        b = random.choice(verified_businesses)
        key = (inv.pk, b.pk)
        if key in seen:
            continue
        seen.add(key)
        pairs.append((inv, b))
    for idx, (inv, business) in enumerate(pairs):
        req, created = InvestorConnectionRequest.objects.get_or_create(
            entrepreneur=business.entrepreneur,
            business=business,
            investor=inv,
            defaults={
                "message": f"We'd love to share our pitch for {business.name}.",
            },
        )
        if not created:
            continue
        if idx % 3 == 0:
            try:
                accept_investor_request(req, by_user=admin)
                accepted += 1
            except Exception as exc:  # noqa: BLE001
                logger.warning("seed accept failed: %s", exc)
        elif idx % 3 == 1:
            req.decline(by_user=admin, reason="Not a thesis fit at the moment.")
            req.save()
            declined += 1
        else:
            pending += 1
    return {
        "requests": InvestorConnectionRequest.objects.count(),
        "accepted": accepted,
        "declined": declined,
        "pending": pending,
    }


def _seed_manual_funding(admin: User, investors: list[Investor]) -> int:
    verified_businesses = list(
        Business.objects.filter(
            verification_status=Business.Status.VERIFIED,
        ).select_related("entrepreneur")[:5]
    )
    verified_invs = [i for i in investors if i.verification_status == Investor.Status.VERIFIED]
    if not verified_businesses:
        return 0
    created_count = 0
    for idx, business in enumerate(verified_businesses):
        if ManualFundingRecord.objects.filter(business=business).exists():
            continue
        funder_name = (
            verified_invs[idx % len(verified_invs)].org_name
            if verified_invs else "Off-platform donor"
        )
        ManualFundingRecord.objects.create(
            entrepreneur=business.entrepreneur,
            business=business,
            funder_name=funder_name,
            investor=verified_invs[idx % len(verified_invs)] if verified_invs else None,
            amount=Decimal(random.choice([5000, 10000, 25000, 50000, 100000])),
            currency="USD",
            funded_at=(timezone.now() - timedelta(days=30 + idx * 14)).date(),
            source_note="Off-platform funding event captured by admin.",
            captured_by_admin=admin,
        )
        created_count += 1
    return created_count


@transaction.atomic
def seed() -> dict:
    admin = _ensure_admin()
    investors = _seed_investors(admin)
    calls = _seed_funding_calls(admin, investors)
    apps_summary = _seed_applications(calls, admin)
    requests_summary = _seed_connection_requests(investors, admin)
    manual_count = _seed_manual_funding(admin, investors)
    matches_refreshed = _seed_matches()
    return {
        "investors": Investor.objects.count(),
        "verified_investors": Investor.objects.filter(
            verification_status=Investor.Status.VERIFIED,
        ).count(),
        "funding_calls": FundingCall.objects.count(),
        "published_calls": FundingCall.objects.filter(
            status=FundingCall.Status.PUBLISHED,
        ).count(),
        "funding_applications": apps_summary.get("applications", 0),
        "investor_requests": requests_summary.get("requests", 0),
        "requests_accepted": requests_summary.get("accepted", 0),
        "manual_funding_records": ManualFundingRecord.objects.count(),
        "matches_refreshed_for_entrepreneurs": matches_refreshed,
    }
