from dataclasses import dataclass
from datetime import UTC, datetime, timedelta
from hashlib import sha256

from zbt.commands.seed import build_seed_manifest, stable_id
from zbt.core.config import Settings
from zbt.domains.attribution.models import LeadAttribution, OrderAttribution
from zbt.domains.enrollment.models import (
    EnrollmentDraft,
    EnrollmentOrder,
    PaymentTransaction,
    Policy,
    Quote,
    UserConfirmation,
)
from zbt.domains.identity.models import H5User
from zbt.infrastructure.mysql.attribution_repositories import (
    SqlAlchemyAttributionRepository,
)
from zbt.infrastructure.mysql.enrollment_repositories import (
    SqlAlchemyEnrollmentRepository,
)
from zbt.infrastructure.mysql.repositories import SqlAlchemyIdentityRepository
from zbt.infrastructure.mysql.sessions import (
    DatabaseName,
    create_mysql_engine,
    create_session_factory,
)


@dataclass(frozen=True, slots=True)
class BusinessCase:
    applicant: str
    insured: str
    relationship: str
    product_code: str
    plan_code: str
    status: str
    days_ago: int
    policy_status: str | None = None
    payment_status: str | None = None


BASE_CASES: tuple[BusinessCase, ...] = (
    BusinessCase(
        "周明远", "周明远", "SELF", "MED-BASIC", "BASIC", "ISSUED", 0, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "林晓雨",
        "林建国",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "ISSUED",
        1,
        "ACTIVE",
        "SUCCEEDED",
    ),
    BusinessCase(
        "陈子昂", "王思雅", "SPOUSE", "MED-UPGRADE", "STANDARD", "ISSUED", 2, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "赵清和", "赵安安", "CHILD", "ACC-FAMILY", "FAMILY", "ISSUED", 3, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "许文博", "许文博", "SELF", "ACC-FAMILY", "INDIVIDUAL", "ISSUED", 4, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "郑嘉宁",
        "郑国强",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "ISSUED",
        5,
        "ACTIVE",
        "SUCCEEDED",
    ),
    BusinessCase(
        "孙语桐", "方俊杰", "SPOUSE", "MED-UPGRADE", "ENHANCED", "ISSUED", 7, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "何志诚", "何沐阳", "CHILD", "ACC-FAMILY", "FAMILY", "ISSUED", 9, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "蒋若溪", "蒋若溪", "SELF", "MED-UPGRADE", "STANDARD", "ISSUED", 12, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "唐景行",
        "唐德华",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "ISSUED",
        15,
        "ACTIVE",
        "SUCCEEDED",
    ),
    BusinessCase(
        "邓舒涵", "宋嘉树", "SPOUSE", "MED-BASIC", "BASIC", "ISSUED", 20, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "梁书航", "梁小满", "CHILD", "ACC-FAMILY", "FAMILY", "ISSUED", 25, "ACTIVE", "SUCCEEDED"
    ),
    BusinessCase(
        "顾念安", "顾念安", "SELF", "MED-BASIC", "BASIC", "ISSUED", 30, "CANCELLED", "SUCCEEDED"
    ),
    BusinessCase(
        "马致远",
        "马国庆",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "ISSUED",
        430,
        "EXPIRED",
        "SUCCEEDED",
    ),
    BusinessCase(
        "罗婉清", "罗婉清", "SELF", "MED-UPGRADE", "ENHANCED", "PAID", 1, None, "SUCCEEDED"
    ),
    BusinessCase("谢云舟", "沈知夏", "SPOUSE", "MED-BASIC", "BASIC", "PAID", 3, None, "SUCCEEDED"),
    BusinessCase(
        "苏曼青",
        "苏曼青",
        "SELF",
        "ACC-FAMILY",
        "INDIVIDUAL",
        "PENDING_PAYMENT",
        0,
        None,
        "CREATED",
    ),
    BusinessCase(
        "白敬亭",
        "白国良",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "PENDING_PAYMENT",
        2,
        None,
        None,
    ),
    BusinessCase(
        "陆星河", "陆小禾", "CHILD", "ACC-FAMILY", "FAMILY", "PENDING_PAYMENT", 5, None, "CREATED"
    ),
    BusinessCase(
        "秦知意", "秦知意", "SELF", "MED-UPGRADE", "STANDARD", "PENDING_PAYMENT", 8, None, None
    ),
    BusinessCase(
        "夏予川", "顾安然", "SPOUSE", "MED-BASIC", "BASIC", "PENDING_PAYMENT", 11, None, "CREATED"
    ),
    BusinessCase("叶听澜", "叶听澜", "SELF", "MED-BASIC", "BASIC", "CANCELLED", 4, None, "FAILED"),
    BusinessCase(
        "程砚秋",
        "程建军",
        "PARENT",
        "MED-SENIOR",
        "SENIOR_STANDARD",
        "CANCELLED",
        13,
        None,
        "FAILED",
    ),
    BusinessCase("江望舒", "江小鱼", "CHILD", "ACC-FAMILY", "FAMILY", "CANCELLED", 21, None, None),
)

GENERATED_NAMES = (
    "沈亦辰",
    "温书瑶",
    "乔景川",
    "孟知微",
    "裴言澈",
    "楚星晚",
    "傅时安",
    "宋予宁",
    "贺云深",
    "姜晚晴",
    "顾承泽",
    "许清欢",
    "陆闻舟",
    "苏念初",
    "程屿森",
    "叶舒然",
    "周砚北",
    "林语棠",
    "陈遇白",
    "赵南乔",
    "唐慕川",
    "梁初夏",
    "郑怀瑾",
    "孙云舒",
    "何景明",
    "蒋依澜",
    "邓星野",
    "罗芷晴",
    "马明谦",
    "谢知夏",
    "秦远洲",
    "白若宁",
    "夏云川",
    "顾安禾",
    "叶庭深",
    "程书妍",
    "江叙白",
    "周晚意",
    "林嘉树",
    "陈思源",
    "赵安澜",
    "唐清越",
    "梁知行",
    "郑初晴",
    "孙景和",
    "何语心",
    "蒋承安",
    "邓若溪",
)


def _generated_business_cases() -> tuple[BusinessCase, ...]:
    relationships = ("SELF", "PARENT", "SPOUSE", "CHILD")
    product_plans = (
        ("MED-BASIC", "BASIC"),
        ("MED-BASIC", "BASIC"),
        ("MED-UPGRADE", "STANDARD"),
        ("MED-UPGRADE", "ENHANCED"),
        ("MED-SENIOR", "SENIOR_STANDARD"),
        ("ACC-FAMILY", "INDIVIDUAL"),
        ("ACC-FAMILY", "FAMILY"),
    )
    generated: list[BusinessCase] = []
    for offset, applicant in enumerate(GENERATED_NAMES, start=1):
        index = len(BASE_CASES) + offset
        relationship = relationships[index % len(relationships)]
        product_code, plan_code = product_plans[index % len(product_plans)]
        if product_code == "MED-SENIOR":
            relationship = "PARENT"
        selector = index % 20
        if selector in {0, 7, 14, 18}:
            status = "PENDING_PAYMENT"
            policy_status = None
            payment_status = "CREATED" if index % 2 else None
        elif selector in {9, 19}:
            status = "CANCELLED"
            policy_status = None
            payment_status = "FAILED" if index % 2 else None
        elif selector == 11:
            status = "PAID"
            policy_status = None
            payment_status = "SUCCEEDED"
        else:
            status = "ISSUED"
            policy_status = "ACTIVE"
            payment_status = "SUCCEEDED"
        insured = (
            applicant
            if relationship == "SELF"
            else GENERATED_NAMES[(offset + 11) % len(GENERATED_NAMES)]
        )
        generated.append(
            BusinessCase(
                applicant=applicant,
                insured=insured,
                relationship=relationship,
                product_code=product_code,
                plan_code=plan_code,
                status=status,
                days_ago=(index * 7 + (index // 3) * 5) % 120,
                policy_status=policy_status,
                payment_status=payment_status,
            )
        )
    return tuple(generated)


CASES = BASE_CASES + _generated_business_cases()

ATTRIBUTION_PATTERN: tuple[int | None, ...] = (
    0,
    0,
    0,
    1,
    0,
    1,
    2,
    0,
    1,
    2,
    3,
    0,
    1,
    2,
    3,
    4,
    0,
    1,
    2,
    3,
    4,
    5,
    None,
    None,
)

LEAD_ONLY_COUNT = 42

PREMIUMS = {
    ("MED-BASIC", "BASIC"): 23900,
    ("MED-UPGRADE", "STANDARD"): 39900,
    ("MED-UPGRADE", "ENHANCED"): 59900,
    ("MED-SENIOR", "SENIOR_STANDARD"): 19900,
    ("ACC-FAMILY", "INDIVIDUAL"): 9900,
    ("ACC-FAMILY", "FAMILY"): 15900,
}


def run_seed(settings: Settings | None = None) -> dict[str, int]:
    resolved_settings = settings or Settings()
    engine = create_mysql_engine(resolved_settings, DatabaseName.CORE)
    factory = create_session_factory(engine)
    identities = SqlAlchemyIdentityRepository(factory)
    enrollment = SqlAlchemyEnrollmentRepository(factory)
    attribution = SqlAlchemyAttributionRepository(factory)
    manifest = build_seed_manifest()
    for salesperson in manifest.salespersons:
        attribution.save_salesperson(salesperson)
    for seed_code in manifest.promotion_codes:
        attribution.save_promotion_code(seed_code)
    products = {product.product_code: product for product in manifest.products}
    published_versions = {
        product.product_code: next(
            version
            for version in manifest.versions
            if version.product_id == product.id and version.status == "PUBLISHED"
        )
        for product in manifest.products
    }
    anchor = datetime(2026, 7, 26, 16, 30, tzinfo=UTC)
    customer_ids: dict[str, str] = {}

    for index, case in enumerate(CASES, start=1):
        mobile = f"1886001{index:04d}"
        user_id = stable_id(f"business-user:{mobile}")
        customer_ids[mobile] = user_id
        identities.save_h5_user(
            H5User(
                id=user_id,
                mobile=mobile,
                mobile_masked=f"{mobile[:3]}****{mobile[-4:]}",
                display_name=case.applicant,
                status="ACTIVE",
                created_at=anchor - timedelta(days=case.days_ago + 8),
            )
        )
        promotion_index = ATTRIBUTION_PATTERN[(index - 1) % len(ATTRIBUTION_PATTERN)]
        attributed_code = (
            manifest.promotion_codes[promotion_index] if promotion_index is not None else None
        )
        if attributed_code is not None:
            attribution.save_lead(
                LeadAttribution(
                    id=stable_id(f"business-lead:{index}"),
                    h5_user_id=user_id,
                    salesperson_id=attributed_code.salesperson_id,
                    promotion_code_id=attributed_code.id,
                    source_code=attributed_code.code,
                    visit_count=1 + index % 4,
                    first_touch_at=anchor - timedelta(days=case.days_ago + 8),
                    last_touch_at=anchor - timedelta(days=case.days_ago + 1),
                )
            )

        product = products[case.product_code]
        version = published_versions[case.product_code]
        plan = next(item for item in version.plans if item.code == case.plan_code)
        created_at = anchor - timedelta(
            days=case.days_ago,
            hours=(index * 3) % 12,
            minutes=(index * 7) % 60,
        )
        premium_cents = PREMIUMS[(case.product_code, case.plan_code)]
        quote_id = stable_id(f"business-quote:{index}")
        draft_id = stable_id(f"business-draft:{index}")
        confirmation_id = stable_id(f"business-confirmation:{index}")
        order_id = stable_id(f"business-order:{index}")
        id_no_suffix = f"{index:04d}"
        applicant_snapshot = {
            "name": case.applicant,
            "id_no": f"51010419880101{id_no_suffix}",
        }
        insured_snapshot = {
            "name": case.insured,
            "id_no": f"51010419920102{id_no_suffix}",
        }

        enrollment.save_quote(
            Quote(
                id=quote_id,
                user_id=user_id,
                product_id=product.id,
                product_version_id=version.id,
                plan_id=plan.id,
                insured_age=65 if case.relationship == "PARENT" else 35,
                insured_region_code="510100",
                occupation_code="GENERAL",
                relationship=case.relationship,
                premium_cents=premium_cents,
                currency="CNY",
                rule_version="eligibility-v1",
                rate_version="rate-v1",
                status="USED",
                expires_at=created_at + timedelta(minutes=30),
                created_at=created_at - timedelta(minutes=12),
            )
        )
        enrollment.save_draft(
            EnrollmentDraft(
                id=draft_id,
                user_id=user_id,
                quote_id=quote_id,
                applicant=applicant_snapshot,
                insured=insured_snapshot,
                contact={"mobile": mobile},
                status="CONFIRMED",
                expires_at=created_at + timedelta(hours=24),
                created_at=created_at - timedelta(minutes=8),
            )
        )
        enrollment.save_confirmation(
            UserConfirmation(
                id=confirmation_id,
                user_id=user_id,
                draft_id=draft_id,
                token_hash=sha256(f"business-confirmation-token:{index}".encode()).hexdigest(),
                status="USED",
                expires_at=created_at + timedelta(minutes=10),
                created_at=created_at - timedelta(minutes=3),
            )
        )
        order = EnrollmentOrder(
            id=order_id,
            order_no=f"ORD-{created_at:%Y%m%d}-B{index:07d}",
            user_id=user_id,
            quote_id=quote_id,
            draft_id=draft_id,
            confirmation_id=confirmation_id,
            idempotency_key=f"business-order-{index:04d}",
            product_id=product.id,
            product_version_id=version.id,
            plan_id=plan.id,
            applicant_snapshot=applicant_snapshot,
            insured_snapshot=insured_snapshot,
            amount_cents=premium_cents,
            currency="CNY",
            status=case.status,
            created_at=created_at,
        )
        enrollment.save_order(order)
        if attributed_code is not None:
            attribution.save_order_attribution(
                OrderAttribution(
                    id=stable_id(f"business-order-attribution:{index}"),
                    order_id=order.id,
                    h5_user_id=user_id,
                    salesperson_id=attributed_code.salesperson_id,
                    promotion_code_id=attributed_code.id,
                    source_code=attributed_code.code,
                    amount_cents=premium_cents,
                    attributed_at=created_at,
                )
            )

        if case.payment_status is not None:
            payment_id = stable_id(f"business-payment:{index}")
            succeeded_at = (
                created_at + timedelta(minutes=2) if case.payment_status == "SUCCEEDED" else None
            )
            enrollment.save_payment(
                PaymentTransaction(
                    id=payment_id,
                    payment_no=f"PAY-{created_at:%Y%m%d}-B{index:07d}",
                    order_id=order_id,
                    user_id=user_id,
                    transaction_type="PAYMENT",
                    provider="LOCAL_MOCK_PAY",
                    idempotency_key=f"business-payment-{index:04d}",
                    amount_cents=premium_cents,
                    currency="CNY",
                    status=case.payment_status,
                    provider_transaction_no=(
                        f"MP-{created_at:%Y%m%d}-B{index:07d}"
                        if case.payment_status == "SUCCEEDED"
                        else None
                    ),
                    succeeded_at=succeeded_at,
                    created_at=created_at + timedelta(minutes=1),
                )
            )

        if case.policy_status is not None:
            coverage_start = (created_at + timedelta(days=1)).date()
            coverage_end = coverage_start + timedelta(days=365)
            enrollment.save_policy(
                Policy(
                    id=stable_id(f"business-policy:{index}"),
                    policy_no=f"POL-{created_at:%Y%m%d}-B{index:07d}",
                    order_id=order_id,
                    user_id=user_id,
                    product_id=product.id,
                    product_version_id=version.id,
                    plan_id=plan.id,
                    premium_cents=premium_cents,
                    currency="CNY",
                    coverage_start=coverage_start,
                    coverage_end=coverage_end,
                    status=case.policy_status,
                    issued_at=created_at + timedelta(minutes=4),
                )
            )

    for index in range(1, LEAD_ONLY_COUNT + 1):
        mobile = f"1887002{index:04d}"
        user_id = stable_id(f"business-lead-only-user:{mobile}")
        promotion_index = ATTRIBUTION_PATTERN[
            (index * 5 + index // 4) % (len(ATTRIBUTION_PATTERN) - 2)
        ]
        if promotion_index is None:
            continue
        promotion_code = manifest.promotion_codes[promotion_index]
        created_days_ago = (index * 11 + 3) % 150
        identities.save_h5_user(
            H5User(
                id=user_id,
                mobile=mobile,
                mobile_masked=f"{mobile[:3]}****{mobile[-4:]}",
                display_name=f"咨询客户{index:02d}",
                status="ACTIVE",
                created_at=anchor - timedelta(days=created_days_ago),
            )
        )
        attribution.save_lead(
            LeadAttribution(
                id=stable_id(f"business-lead-only:{index}"),
                h5_user_id=user_id,
                salesperson_id=promotion_code.salesperson_id,
                promotion_code_id=promotion_code.id,
                source_code=promotion_code.code,
                visit_count=1 + (index * 3) % 7,
                first_touch_at=anchor - timedelta(days=created_days_ago),
                last_touch_at=anchor - timedelta(days=max(0, created_days_ago - (index % 12))),
            )
        )

    engine.dispose()
    return {
        "customers": len(customer_ids) + LEAD_ONLY_COUNT,
        "orders": len(CASES),
        "policies": sum(case.policy_status is not None for case in CASES),
        "payments": sum(case.payment_status is not None for case in CASES),
        "leads": sum(
            ATTRIBUTION_PATTERN[(index - 1) % len(ATTRIBUTION_PATTERN)] is not None
            for index in range(1, len(CASES) + 1)
        )
        + LEAD_ONLY_COUNT,
        "attributed_orders": sum(
            ATTRIBUTION_PATTERN[(index - 1) % len(ATTRIBUTION_PATTERN)] is not None
            for index in range(1, len(CASES) + 1)
        ),
    }


if __name__ == "__main__":
    result = run_seed()
    print(
        "业务数据写入完成："
        f"{result['customers']} 位投保用户，"
        f"{result['orders']} 笔订单，"
        f"{result['payments']} 条支付流水，"
        f"{result['policies']} 张保单，"
        f"{result['leads']} 条推广线索，"
        f"{result['attributed_orders']} 笔推广归因订单。"
    )
