# -*- coding: utf-8 -*-
"""幂等种子数据：角色 / 权限 / 角色权限矩阵 / 区域 / 组织 / 演示用户。

在 ``app.db.init_db()`` 之后调用；仅当 ``roles`` 表为空时才写入，
避免覆盖已有数据。
"""
from __future__ import annotations

import json

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from .models import (
    AuditLog,
    ContentItem,
    FinanceRecord,
    InvestmentIntent,
    InvestorPreference,
    Message,
    Notification,
    OpcProfile,
    OpcTask,
    Organization,
    OrganizationMember,
    Permission,
    PortalDashboard,
    PortalPage,
    Region,
    Roadshow,
    RoadshowRegistration,
    Role,
    RolePermission,
    ServiceProvider,
    ServiceReferral,
    SessionToken,
    SubsidyApplication,
    SystemConfig,
    Task,
    TrainingEnrollment,
    User,
    UserIdentity,
    Agent,
)
from .repositories import AGENT_SEED, new_id, utcnow_iso
from .security import hash_password

# ---------------------------------------------------------------------------
# 角色
# ---------------------------------------------------------------------------
ROLES = [
    # 业务角色
    ("opc_member", "OPC超级个体", "business"),
    ("carrier", "创业载体（园区）", "business"),
    ("enterprise", "甲方企业", "business"),
    ("provider", "服务商", "business"),
    ("government", "政务端", "business"),
    ("operator", "平台运营方", "business"),
    # 政务子级（继承 government 通用权限）
    ("government|gov_province", "省级政务", "government-level"),
    ("government|gov_city", "市级政务", "government-level"),
    ("government|gov_district", "区县级政务", "government-level"),
    ("government|gov_org_admin", "政务组织机构管理员", "government-level"),
    ("government|gov_op_account", "政务操作账号", "government-level"),
    # 运营方内部子角色
    ("operator|op_super_admin", "超级管理员", "operator-internal"),
    ("operator|op_admin", "运营管理员", "operator-internal"),
    ("operator|op_customer_service", "客服管理员", "operator-internal"),
    ("operator|op_finance", "财务管理员", "operator-internal"),
    ("operator|op_analyst", "数据分析师", "operator-internal"),
    ("operator|op_techops", "技术运维", "operator-internal"),
    # 企业 / 载体 / 服务商 / OPC 端口内子角色
    ("enterprise|admin", "企业管理员", "business"),
    ("enterprise|operator", "企业操作者", "business"),
    ("carrier|admin", "园区管理员", "business"),
    ("carrier|operator", "园区操作者", "business"),
    ("provider|admin", "服务商管理员", "business"),
    ("provider|operator", "服务商操作者", "business"),
    ("opc_member|certified", "认证OPC", "business"),
    ("opc_member|independent", "独立OPC", "business"),
    # 投资人端口内子角色
    ("investor", "投资人", "business"),
    ("investor|org_director", "机构投资总监", "business"),
    ("investor|org_manager", "机构投资经理", "business"),
    ("investor|personal", "个人投资人", "business"),
    # 开放平台端口内子角色
    ("developer", "开发者", "business"),
    ("developer|dev_individual", "个人开发者", "business"),
    ("developer|dev_org_admin", "机构开发者管理员", "business"),
    ("developer|dev_member", "开发者成员", "business"),
]

# ---------------------------------------------------------------------------
# 权限
# ---------------------------------------------------------------------------
PERMISSIONS = [
    # 各门户菜单
    ("menu:opc_dashboard", "OPC 门户", "menu", "opc"),
    ("menu:carrier_dashboard", "载体门户", "menu", "carrier"),
    ("menu:enterprise_dashboard", "企业门户", "menu", "enterprise"),
    ("menu:provider_dashboard", "服务商门户", "menu", "provider"),
    ("menu:government_dashboard", "政务门户", "menu", "government"),
    ("menu:operator_dashboard", "运营门户", "menu", "operator"),
    ("menu:investor_dashboard", "投资人门户", "menu", "investor"),
    ("menu:investor_projects", "投资人·项目库", "menu", "investor"),
    ("menu:investor_roadshow", "投资人·路演活动", "menu", "investor"),
    ("menu:investor_training", "投资人·投融资培训", "menu", "investor"),
    ("menu:developer_dashboard", "开发者门户", "menu", "developer"),
    ("menu:developer_apps", "开发者·我的应用", "menu", "developer"),
    ("menu:developer_plugins", "开发者·我的插件", "menu", "developer"),
    ("menu:developer_skills", "开发者·技能市场", "menu", "developer"),
    # 管理菜单
    ("menu:admin_user_mgmt", "用户管理", "menu", "admin"),
    ("menu:admin_role_mgmt", "角色权限管理", "menu", "admin"),
    ("menu:gov_data", "政务数据", "menu", "government"),
    ("menu:audit", "审计日志", "menu", "audit"),
    ("menu:settlements", "结算台账", "menu", "operator"),
    # 操作
    ("action:user.assign_role", "分配角色", "action", "admin"),
    ("action:user.disable", "禁用/启用用户", "action", "admin"),
    ("action:role.grant_perm", "配置角色权限", "action", "admin"),
    ("action:org.manage", "管理组织", "action", "org"),
    ("action:data.export", "数据导出", "action", "data"),
    ("action:audit.view", "查看审计", "action", "audit"),
    ("action:task.manage", "任务管理（建改）", "action", "operator"),
    ("action:provider.manage", "服务商管理（建改）", "action", "operator"),
    ("action:content.manage", "内容管理（建发）", "action", "operator"),
    ("action:config.manage", "系统配置（修改）", "action", "operator"),
    ("action:settlement.manage", "结算管理", "action", "operator"),
]

# role_id -> [permission_id]
ROLE_PERMISSIONS: dict[str, list[str]] = {
    "opc_member": ["menu:opc_dashboard"],
    "carrier": ["menu:carrier_dashboard", "action:org.manage"],
    "enterprise": ["menu:enterprise_dashboard", "action:org.manage"],
    "provider": [
        "menu:provider_dashboard", "action:org.manage", "action:data.export",
    ],
    "government": ["menu:government_dashboard", "menu:gov_data"],
    "government|gov_province": ["action:data.export"],
    "government|gov_city": ["action:data.export"],
    "government|gov_district": ["action:data.export"],
    "government|gov_org_admin": ["action:org.manage"],
    "government|gov_op_account": ["action:data.export"],
    "operator": ["menu:operator_dashboard"],
    "operator|op_super_admin": [
        "menu:admin_user_mgmt", "menu:admin_role_mgmt", "menu:audit", "menu:settlements",
        "action:user.assign_role", "action:user.disable", "action:role.grant_perm",
        "action:org.manage", "action:data.export", "action:audit.view",
        "action:task.manage", "action:provider.manage", "action:content.manage",
        "action:config.manage", "action:settlement.manage",
    ],
    "operator|op_admin": [
        "menu:admin_user_mgmt", "menu:admin_role_mgmt", "menu:audit", "menu:settlements",
        "action:user.assign_role", "action:user.disable",
        "action:org.manage", "action:data.export", "action:audit.view",
        "action:task.manage", "action:provider.manage", "action:content.manage",
        "action:config.manage", "action:settlement.manage",
    ],
    "operator|op_customer_service": [
        "action:data.export", "action:task.manage", "action:provider.manage",
        "action:content.manage",
    ],
    "operator|op_finance": [
        "menu:settlements", "action:data.export", "action:settlement.manage",
    ],
    "operator|op_analyst": ["menu:audit", "action:data.export", "action:audit.view"],
    "operator|op_techops": [
        "menu:admin_user_mgmt", "action:org.manage", "action:data.export",
        "action:config.manage",
    ],
    "enterprise|admin": ["menu:enterprise_dashboard", "action:org.manage"],
    "enterprise|operator": ["menu:enterprise_dashboard"],
    "carrier|admin": ["menu:carrier_dashboard", "action:org.manage"],
    "carrier|operator": ["menu:carrier_dashboard"],
    "provider|admin": [
        "menu:provider_dashboard", "action:org.manage", "action:data.export",
    ],
    "provider|operator": ["menu:provider_dashboard", "action:data.export"],
    "opc_member|certified": ["menu:opc_dashboard"],
    "opc_member|independent": ["menu:opc_dashboard"],
    "investor": ["menu:investor_dashboard"],
    "investor|org_director": [
        "menu:investor_dashboard", "menu:investor_projects", "menu:investor_roadshow", "menu:investor_training",
        "action:preference.manage", "action:project.view", "action:intent.manage",
        "action:roadshow.publish", "action:roadshow.host", "action:invest.decide",
    ],
    "investor|org_manager": [
        "menu:investor_dashboard", "menu:investor_projects", "menu:investor_roadshow",
        "action:preference.manage", "action:project.view", "action:intent.manage", "action:roadshow.attend",
    ],
    "investor|personal": [
        "menu:investor_dashboard", "menu:investor_projects", "menu:investor_roadshow", "menu:investor_training",
        "action:preference.manage", "action:project.view", "action:intent.manage",
        "action:roadshow.attend", "action:roadshow.register",
    ],
    "developer": ["menu:developer_dashboard"],
    "developer|dev_individual": [
        "menu:developer_dashboard", "menu:developer_apps", "menu:developer_plugins", "menu:developer_skills",
        "action:app.create", "action:app.publish", "action:plugin.create", "action:skill.publish",
    ],
    "developer|dev_org_admin": [
        "menu:developer_dashboard", "menu:developer_apps", "menu:developer_plugins", "menu:developer_skills",
        "action:app.create", "action:app.publish", "action:app.manage", "action:plugin.create", "action:skill.publish", "action:apikey.manage",
    ],
    "developer|dev_member": [
        "menu:developer_dashboard", "menu:developer_apps",
        "action:app.create",
    ],
}

# ---------------------------------------------------------------------------
# 区域（省/市/区县 示例树）
# ---------------------------------------------------------------------------
REGIONS = [
    ("r_prov_yn", "云南省", "province", None),
    ("r_city_km", "昆明市", "city", "r_prov_yn"),
    ("r_city_qj", "曲靖市", "city", "r_prov_yn"),
    ("r_dist_pl", "盘龙区", "district", "r_city_km"),
    ("r_dist_wh", "五华区", "district", "r_city_km"),
    ("r_dist_ql", "麒麟区", "district", "r_city_qj"),
]

# ---------------------------------------------------------------------------
# 组织（甲方企业 / 创业载体 / 服务商）
# ---------------------------------------------------------------------------
ORGANIZATIONS = [
    ("o_ent_001", "云南某某甲方企业", "enterprise", "r_dist_pl", None),
    ("o_car_001", "滇池创业园", "carrier", "r_dist_wh", None),
    ("o_pro_001", "云超财税服务所", "provider", "r_city_km", None),
    ("o_ent_prov", "省级示范企业", "enterprise", "r_prov_yn", None),
]

# ---------------------------------------------------------------------------
# 演示用户（role/sub_role/org/region 各异，用于 RBAC 演示与测试）
# ---------------------------------------------------------------------------
DEMO_USERS = [
    # id, username, nickname, password, role, sub_role, org_id, region_id
    ("u_demo_01", "pine", "云超服小助手", "123456", "operator", "op_super_admin", None, "r_prov_yn"),
    ("u_gov_prov", "gov_prov", "省政务账号", "123456", "government", "gov_province", None, "r_prov_yn"),
    ("u_gov_dist", "gov_dist", "区县政务账号", "123456", "government", "gov_district", None, "r_dist_wh"),
    ("u_ent_01", "ent01", "甲方企业账号", "123456", "enterprise", None, "o_ent_001", "r_dist_pl"),
    ("u_car_01", "car01", "园区运营账号", "123456", "carrier", None, "o_car_001", "r_dist_wh"),
    ("u_pro_01", "pro01", "服务商账号", "123456", "provider", None, "o_pro_001", "r_city_km"),
    ("u_op_an", "op_analyst", "运营数据分析", "123456", "operator", "op_analyst", None, "r_prov_yn"),
    ("u_op_admin", "op_admin", "运营管理员", "123456", "operator", "op_admin", None, "r_prov_yn"),
    ("u_op_cs", "op_cs", "运营客服", "123456", "operator", "op_customer_service", None, "r_prov_yn"),
    ("u_op_fin", "op_fin", "运营财务", "123456", "operator", "op_finance", None, "r_prov_yn"),
    ("u_op_tech", "op_tech", "运营运维", "123456", "operator", "op_techops", None, "r_prov_yn"),
    ("u_opc_01", "opc01", "OPC个人创业者", "123456", "opc_member", None, None, "r_dist_wh"),
    ("u_opc_02", "opc02", "OPC个体工商户", "123456", "opc_member", None, None, "r_dist_pl"),
    ("u_inv_01", "inv01", "投资人账号", "123456", "investor", "personal", None, "r_prov_yn"),
    ("u_inv_02", "inv02", "机构投资人", "123456", "investor", "org_director", None, "r_prov_yn"),
]


async def _add_agent(session: AsyncSession, seed: dict, user_id: str, port: str | None, now: str) -> None:
    """幂等播种单个智能体：按 (id,user_id,port) 已存在则跳过（修全局固定 id 多用户/端口填重）。"""
    key = (seed["id"], user_id, port)
    if await session.get(Agent, key) is not None:
        return
    session.add(
        Agent(id=seed["id"], user_id=user_id, port=port, name=seed["name"],
              description=seed["description"], language=seed.get("language", "zh"),
              model_name=seed.get("model_name", ""), deletable=seed.get("deletable", True),
              use_fixed_soul=seed.get("use_fixed_soul", False),
              created_at=now, updated_at=now)
    )


async def seed_data(session: AsyncSession) -> None:
    """幂等种子：仅当 roles 表为空时写入全部基础数据；身份与 OPC 业务始终补种。"""
    now = utcnow_iso()
    existing = await session.scalar(select(Role.id).limit(1))
    if existing is not None:
        # 已初始化的库：仍补种 OPC 业务演示数据、账号身份与端口工作台（各自幂等）。
        await _sync_permissions(session, now)
        await _ensure_extra_demo_users(session, now)
        await _seed_opc_business(session, now)
        await _migrate_identities(session, now)
        await _seed_port_dashboards(session, now)
        await _seed_market(session, now)
        await _seed_port_pages(session, now)
        await _seed_investor(session, now)
        await _seed_ecosystem(session, now)
        await _ensure_port_agents(session, now)
        await _seed_org_members(session, now)
        await session.commit()
        return

    session.add_all(
        [Role(id=r[0], name=r[1], scope_type=r[2]) for r in ROLES]
    )
    session.add_all(
        [
            Permission(id=p[0], name=p[1], category=p[2], module=p[3])
            for p in PERMISSIONS
        ]
    )
    for role_id, perms in ROLE_PERMISSIONS.items():
        session.add_all(
            [
                RolePermission(role_id=role_id, permission_id=perm)
                for perm in perms
            ]
        )
    session.add_all(
        [
            Region(id=r[0], name=r[1], level=r[2], parent_id=r[3], created_at=now)
            for r in REGIONS
        ]
    )
    session.add_all(
        [
            Organization(
                id=o[0], name=o[1], type=o[2], region_id=o[3],
                parent_id=o[4], created_at=now,
            )
            for o in ORGANIZATIONS
        ]
    )

    for (uid, username, nickname, password, role, sub_role, org_id, region_id) in DEMO_USERS:
        digest, salt = hash_password(password)
        session.add(
            User(
                id=uid, username=username, nickname=nickname,
                password_hash=digest, password_salt=salt,
                role=role, sub_role=sub_role, org_id=org_id, region_id=region_id,
                status="active", token_version=0, created_at=now, updated_at=now,
            )
        )
        # 为每个用户播种默认 + QA 智能体（幂等按 (id,user_id,port=null) 防重）
        for seed in AGENT_SEED:
            await _add_agent(session, seed, uid, None, now)

    await _seed_operator_business(session, now)
    await _seed_opc_business(session, now)
    await _migrate_identities(session, now)
    await _seed_port_dashboards(session, now)
    await _seed_market(session, now)
    await _seed_port_pages(session, now)
    await _seed_investor(session, now)
    await _seed_ecosystem(session, now)
    await _ensure_port_agents(session, now)
    await _seed_org_members(session, now)
    await session.commit()


# ---------------------------------------------------------------------------
# 运营端业务演示数据（任务/服务商/内容/配置）
# ---------------------------------------------------------------------------
async def _seed_operator_business(session: AsyncSession, now: str) -> None:
    if await session.scalar(select(Task.id).limit(1)) is not None:
        return
    session.add_all([
        Task(id="task_001", title="电商小程序首页 UI 设计", category="设计创意",
             sub_category="UI设计", description="为电商小程序设计首页 UI，含 5 个页面",
             mode="bid", budget_min=2000, budget_max=5000, deadline="2026-09-01",
             status="published", publisher_org_id="o_ent_001",
             publisher_name="云南某某甲方企业", created_at=now, updated_at=now),
        Task(id="task_002", title="品牌文案写作（月度）", category="文案写作",
             sub_category="内容写作", description="每月 8 篇品牌公众号文案",
             mode="designated", budget_min=3000, budget_max=6000, deadline="2026-12-31",
             status="review", publisher_org_id="o_ent_001",
             publisher_name="云南某某甲方企业", created_at=now, updated_at=now),
        Task(id="task_003", title="小程序前后端开发", category="技术开发",
             sub_category="小程序开发", description="点餐小程序全栈开发，3 期交付",
             mode="bid", budget_min=30000, budget_max=60000, deadline="2026-10-15",
             status="pending", publisher_org_id="o_ent_001",
             publisher_name="云南某某甲方企业", created_at=now, updated_at=now),
    ])
    session.add_all([
        ServiceProvider(id="prov_001", name="云超财税服务所", category="财税服务",
                        org_id="o_pro_001", level="gold", rating=4.8, order_count=320,
                        status="active", contact="service@yuncao.example", created_at=now, updated_at=now),
        ServiceProvider(id="prov_002", name="滇峰法律咨询", category="法律服务",
                        org_id=None, level="premium", rating=4.6, order_count=150,
                        status="active", contact="law@dianfeng.example", created_at=now, updated_at=now),
        ServiceProvider(id="prov_003", name="云岭工商代办", category="工商服务",
                        org_id=None, level="certified", rating=4.3, order_count=88,
                        status="pending", contact="biz@yunling.example", created_at=now, updated_at=now),
    ])
    session.add_all([
        ContentItem(id="cont_001", type="policy", title="云南省一次性创业补贴政策",
                    summary="对首次创业且正常经营 1 年以上的 OPC 发放一次性创业补贴",
                    body="适用对象、申报条件、补贴标准、申报流程……", publisher_id="u_gov_prov",
                    status="published", created_at=now, updated_at=now),
        ContentItem(id="cont_002", type="news", title="平台上线公告",
                    summary="云超服平台正式上线运营", body="欢迎入驻……", publisher_id="u_demo_01",
                    status="published", created_at=now, updated_at=now),
    ])
    session.add_all([
        SystemConfig(key="platform.fee.task", value="0.05", description="任务佣金费率（5%）", updated_at=now),
        SystemConfig(key="platform.fee.provider", value="0.15", description="服务商分成比例（15%）", updated_at=now),
    ])


# ---------------------------------------------------------------------------
# OPC 超级个体业务演示数据（工作台聚合：档案/任务/财务/消息）
# ---------------------------------------------------------------------------
async def _seed_opc_business(session: AsyncSession, now: str) -> None:
    """OPC 个人工作台演示数据（按用户幂等）。"""
    if await session.scalar(select(OpcProfile.user_id).limit(1)) is not None:
        return

    # 档案（信用评分）
    session.add_all([
        OpcProfile(user_id="u_opc_01", credit_score=88, created_at=now, updated_at=now),
        OpcProfile(user_id="u_opc_02", credit_score=82, created_at=now, updated_at=now),
    ])

    # 个人任务看板
    session.add_all([
        OpcTask(id="ot_001", user_id="u_opc_01", title="电商小程序首页 UI 设计",
                status="in_progress", budget=4000, deadline="2026-08-20", progress=65,
                created_at=now, updated_at=now),
        OpcTask(id="ot_002", user_id="u_opc_01", title="品牌公众号月度文案",
                status="urgent", budget=3000, deadline="2026-08-10", progress=30,
                created_at=now, updated_at=now),
        OpcTask(id="ot_003", user_id="u_opc_01", title="Logo 及 VI 视觉规范",
                status="completed", budget=5000, deadline="2026-07-15", progress=100,
                created_at=now, updated_at=now),
        OpcTask(id="ot_004", user_id="u_opc_02", title="宣传视频剪辑",
                status="in_progress", budget=2500, deadline="2026-08-28", progress=45,
                created_at=now, updated_at=now),
    ])

    # 财务流水（月度收入 + 少量支出）
    session.add_all([
        FinanceRecord(id="fin_001", user_id="u_opc_01", category="income", amount=3200,
                      date="2026-02-10", note="UI 设计项目款", created_at=now),
        FinanceRecord(id="fin_002", user_id="u_opc_01", category="income", amount=4500,
                      date="2026-03-12", note="Logo 设计项目款", created_at=now),
        FinanceRecord(id="fin_003", user_id="u_opc_01", category="income", amount=2800,
                      date="2026-04-08", note="文案代写项目款", created_at=now),
        FinanceRecord(id="fin_004", user_id="u_opc_01", category="income", amount=5100,
                      date="2026-05-20", note="小程序 UI 项目款", created_at=now),
        FinanceRecord(id="fin_005", user_id="u_opc_01", category="income", amount=3900,
                      date="2026-06-15", note="电商详情页设计", created_at=now),
        FinanceRecord(id="fin_006", user_id="u_opc_01", category="income", amount=4600,
                      date="2026-07-18", note="H5 营销页设计", created_at=now),
        FinanceRecord(id="fin_007", user_id="u_opc_01", category="expense", amount=600,
                      date="2026-07-02", note="素材版权购买", created_at=now),
    ])

    # 站内消息
    session.add_all([
        Message(id="msg_001", user_id="u_opc_01", msg_type="policy",
                title="政策匹配提醒", content="2 项新政策与您高度匹配，请及时查看申报",
                read=False, created_at=now),
        Message(id="msg_002", user_id="u_opc_01", msg_type="task",
                title="任务状态更新", content="您参与的「电商小程序首页 UI 设计」进度已更新",
                read=False, created_at=now),
        Message(id="msg_003", user_id="u_opc_01", msg_type="service",
                title="服务商回复", content="云超财税服务所已回复您的询价",
                read=True, created_at=now),
        Message(id="msg_004", user_id="u_opc_01", msg_type="agent",
                title="智能体建议", content="您的月度收入较上月增长 18%",
                read=True, created_at=now),
    ])


# ---------------------------------------------------------------------------
# 账号↔端口身份（回填 + 多身份演示）
# ---------------------------------------------------------------------------
_ROLE_TO_PORT = {
    "opc_member": "opc",
    "carrier": "carrier",
    "enterprise": "enterprise",
    "provider": "provider",
    "government": "government",
    "operator": "operator",
}

_SUB_LABELS = {
    "op_super_admin": "超级管理员", "op_admin": "管理员", "op_customer_service": "客服",
    "op_finance": "财务", "op_analyst": "数据分析", "op_techops": "技术运维",
    "gov_province": "省级政务", "gov_city": "市级政务", "gov_district": "区县级政务",
    "gov_org_admin": "组织机构管理员", "gov_op_account": "操作账号",
    "admin": "管理员", "operator": "操作者",
    "certified": "认证OPC", "independent": "独立OPC",
}

_PORT_LABELS = {
    "operator": "运营端", "government": "政务端", "provider": "服务端",
    "enterprise": "企业端", "carrier": "园区端", "opc": "OPC端",
}


def _identity_label(role: str, sub_role: str | None) -> str:
    port = _PORT_LABELS.get(_ROLE_TO_PORT.get(role, role), role)
    sub = _SUB_LABELS.get(sub_role or "", "")
    return f"{port}-{sub}" if sub else port


async def _migrate_identities(session: AsyncSession, now: str) -> None:
    """为既有账号回填端口身份；并为演示账号补充多身份绑定（幂等）。"""
    # session 关闭 autoflush：先 flush 让本会话新增的用户可见
    await session.flush()

    # 1. 每个已有业务角色的用户 -> 一条身份（缺失时补建）
    for u in await session.scalars(select(User)):
        if not u.role:
            continue
        has = await session.scalar(
            select(UserIdentity.id).where(UserIdentity.user_id == u.id).limit(1)
        )
        if has is not None:
            continue
        session.add(
            UserIdentity(
                id=new_id("ident"),
                user_id=u.id,
                port=_ROLE_TO_PORT.get(u.role, u.role),
                role=u.role,
                sub_role=u.sub_role,
                name=_identity_label(u.role, u.sub_role),
                org_id=u.org_id,
                region_id=u.region_id,
                status="active",
                created_at=now, updated_at=now,
            )
        )

    # 2. 演示多身份绑定：ent01 同时具备「企业-管理员」与「OPC-认证」两个身份，
    #    用于验证登录后多身份选择流程。
    ent_extra = await session.scalar(
        select(UserIdentity.id).where(UserIdentity.id == "ident_ent01_opc").limit(1)
    )
    if ent_extra is None and await session.get(User, "u_ent_01") is not None:
        session.add(
            UserIdentity(
                id="ident_ent01_opc",
                user_id="u_ent_01",
                port="opc",
                role="opc_member",
                sub_role="certified",
                name="OPC端-认证OPC",
                org_id=None,
                region_id="r_dist_pl",
                status="active",
                created_at=now, updated_at=now,
            )
        )

    # 3. 超级账号 pine：绑定全部 6 端口身份（演示一账号多端）。
    _PINE_ALL_PORTS = [
        ("ident_pine_gov", "government", "government", "gov_province", "政务端-省级"),
        ("ident_pine_pro", "provider", "provider", "admin", "服务端-管理员"),
        ("ident_pine_ent", "enterprise", "enterprise", "admin", "企业端-管理员"),
        ("ident_pine_car", "carrier", "carrier", "admin", "园区端-管理员"),
        ("ident_pine_opc", "opc", "opc_member", "certified", "OPC端-认证OPC"),
        ("ident_pine_inv", "investor", "investor", "org_director", "投资人端-机构总监"),
        ("ident_pine_dev", "developer", "developer", "dev_org_admin", "开放平台-机构开发者"),
    ]
    for ident_id, port, role, sub_role, name in _PINE_ALL_PORTS:
        if await session.scalar(select(UserIdentity.id).where(UserIdentity.id == ident_id).limit(1)) is not None:
            continue
        if await session.get(User, "u_demo_01") is None:
            break
        session.add(
            UserIdentity(
                id=ident_id, user_id="u_demo_01", port=port, role=role,
                sub_role=sub_role, name=name,
                org_id=None, region_id="r_prov_yn",
                status="active", created_at=now, updated_at=now,
            )
        )


# ---------------------------------------------------------------------------
# 端口工作台聚合数据（企业/载体/服务商/政务）
# ---------------------------------------------------------------------------
_PORT_DASHBOARDS: dict[str, dict] = {
    "enterprise": {
        "stats": [
            {"key": "postedTasks", "value": 56},
            {"key": "activeTasks", "value": 12},
            {"key": "completedTasks", "value": 38},
            {"key": "totalSpent", "value": "¥32.0万"},
            {"key": "matchedTalents", "value": 186},
            {"key": "cooperationRate", "value": "92%"},
        ],
        "list": [
            {"title": "企业官网 UI 设计", "meta": "进行中 · ¥5000-8000 · 12 家投标", "status": "进行中"},
            {"title": "小程序前端开发", "meta": "进行中 · ¥8000-15000 · 8 家投标", "status": "进行中"},
            {"title": "农产品品牌 Logo 设计", "meta": "进行中 · ¥3000-5000 · 15 家投标", "status": "进行中"},
            {"title": "商业计划书撰写", "meta": "进行中 · ¥2000-4000 · 6 家投标", "status": "进行中"},
        ],
    },
    "carrier": {
        "stats": [
            {"key": "residentOPCs", "value": 186},
            {"key": "totalSpace", "value": "5000㎡"},
            {"key": "usedSpace", "value": "4200㎡"},
            {"key": "occupancyRate", "value": "84%"},
            {"key": "serviceOrders", "value": 45},
            {"key": "thisMonthNewOPC", "value": 12},
        ],
        "list": [
            {"title": "云创设计工作室", "meta": "入驻 · 电子信息方向 · 工位 12", "status": "已入驻"},
            {"title": "滇峰数据科技", "meta": "入驻 · 软件服务方向 · 工位 8", "status": "已入驻"},
            {"title": "云南新媒体中心", "meta": "待审批 · 内容创作方向 · 工位 6", "status": "待审批"},
            {"title": "绿野农业电商", "meta": "入驻 · 电商方向 · 工位 10", "status": "已入驻"},
        ],
    },
    "provider": {
        "stats": [
            {"key": "activeServices", "value": 18},
            {"key": "ongoingOrders", "value": 24},
            {"key": "completedOrders", "value": 156},
            {"key": "totalRevenue", "value": "¥52.6万"},
            {"key": "thisMonthIncome", "value": "¥8.56万"},
            {"key": "avgRating", "value": 4.7},
        ],
        "list": [
            {"title": "代理记账服务（月度）", "meta": "进行中 · 云南某某甲方企业 · ¥199/月", "status": "进行中"},
            {"title": "公司注册代办", "meta": "待处理 · 个人创业者 · ¥599/次", "status": "待处理"},
            {"title": "季度财务报表编制", "meta": "已完成 · 云创财税 · ¥2000-3000", "status": "已完成"},
            {"title": "税务筹划咨询", "meta": "进行中 · 鼎信税务所 · ¥1999/次", "status": "进行中"},
        ],
    },
    "government": {
        "stats": [
            {"key": "totalOPC", "value": 45600},
            {"key": "registeredCarriers", "value": 168},
            {"key": "policiesPublished", "value": 245},
            {"key": "policiesMatched", "value": 18200},
            {"key": "tasksInRegion", "value": 12500},
            {"key": "completionRate", "value": "78%"},
        ],
        "list": [
            {"region": "昆明市", "opcCount": 12500, "taskCount": 3200, "completionRate": "82%"},
            {"region": "大理州", "opcCount": 6800, "taskCount": 1800, "completionRate": "76%"},
            {"region": "曲靖市", "opcCount": 5200, "taskCount": 1500, "completionRate": "79%"},
            {"region": "红河州", "opcCount": 4800, "taskCount": 1200, "completionRate": "74%"},
            {"region": "玉溪市", "opcCount": 3500, "taskCount": 900, "completionRate": "81%"},
        ],
        "chart": [
            {"label": "1月", "value": 2400},
            {"label": "2月", "value": 2800},
            {"label": "3月", "value": 3100},
            {"label": "4月", "value": 2900},
            {"label": "5月", "value": 3450},
            {"label": "6月", "value": 3820},
        ],
    },
}


async def _seed_port_dashboards(session: AsyncSession, now: str) -> None:
    """按端口写入工作台 JSON 载荷（幂等：任一端口已存在则跳过）。"""
    if await session.scalar(select(PortalDashboard.port).limit(1)) is not None:
        return
    session.add_all(
        [
            PortalDashboard(
                port=port,
                payload=json.dumps(payload, ensure_ascii=False),
                updated_at=now,
            )
            for port, payload in _PORT_DASHBOARDS.items()
        ]
    )


async def _seed_market(session: AsyncSession, now: str) -> None:
    """补充平台市场数据（任务广场/政策/服务商），幂等（以 task_mk_001 为标记）。"""
    if await session.scalar(select(Task.id).where(Task.id == "task_mk_001").limit(1)) is not None:
        return
    session.add_all([
        Task(id="task_mk_001", title="企业官网 UI 设计", category="设计创意", sub_category="UI设计",
             description="为企业官网设计首页与 3 个内页 UI", mode="bid",
             budget_min=5000, budget_max=8000, deadline="2026-09-10",
             status="published", publisher_org_id="o_ent_001",
             publisher_name="云南某某甲方企业", created_at=now, updated_at=now),
        Task(id="task_mk_002", title="小程序前后端开发", category="技术开发", sub_category="小程序开发",
             description="点餐小程序全栈开发，3 期交付", mode="bid",
             budget_min=30000, budget_max=60000, deadline="2026-10-15",
             status="published", publisher_org_id="o_ent_001",
             publisher_name="云南某某甲方企业", created_at=now, updated_at=now),
        Task(id="task_mk_003", title="农产品品牌 Logo 设计", category="设计创意", sub_category="Logo设计",
             description="为农业合作社设计品牌 Logo", mode="grab",
             budget_min=3000, budget_max=5000, deadline="2026-08-25",
             status="published", publisher_org_id=None,
             publisher_name="大理农业合作社", created_at=now, updated_at=now),
        Task(id="task_mk_004", title="短视频剪辑与制作", category="音视频", sub_category="短视频",
             description="文旅宣传短视频剪辑", mode="grab",
             budget_min=1500, budget_max=3000, deadline="2026-08-20",
             status="published", publisher_org_id=None,
             publisher_name="文旅传媒", created_at=now, updated_at=now),
        Task(id="task_mk_005", title="商业计划书撰写", category="策划/写作", sub_category="文案写作",
             description="创业项目商业计划书", mode="designated",
             budget_min=2000, budget_max=4000, deadline="2026-09-01",
             status="published", publisher_org_id=None,
             publisher_name="创业团队", created_at=now, updated_at=now),
    ])
    session.add_all([
        ContentItem(id="cont_pol_001", type="policy", title="昆明市创业担保贷款政策",
                    summary="对符合条件的创业主体提供担保贷款支持",
                    body="适用对象、额度、利率与申报流程……", publisher_id="u_gov_prov",
                    status="published", created_at=now, updated_at=now),
        ContentItem(id="cont_pol_002", type="policy", title="小微企业税收优惠减免",
                    summary="符合条件的小微企业可享受税收减免",
                    body="适用范围、减免标准与办理方式……", publisher_id="u_gov_prov",
                    status="published", created_at=now, updated_at=now),
        ContentItem(id="cont_pol_003", type="policy", title="云南省一次性创业补贴",
                    summary="首次创业且正常经营 1 年以上发放一次性补贴",
                    body="申报条件、补贴标准与流程……", publisher_id="u_gov_prov",
                    status="published", created_at=now, updated_at=now),
    ])


# ---------------------------------------------------------------------------
# 端口子页面演示数据（企业人才/载体OPC与服务/服务商订单与服务/政务OPC·政策·载体·数据）
# ---------------------------------------------------------------------------
_PORT_PAGES: dict[tuple[str, str], dict] = {
    ("enterprise", "talents"): {
        "items": [
            {"title": "张伟 · UI设计师", "meta": "5年经验 · Figma/PS · 认证OPC", "tag": "认证"},
            {"title": "李娜 · 前端开发", "meta": "3年经验 · Vue/React · 认证OPC", "tag": "认证"},
            {"title": "王强 · 文案策划", "meta": "4年经验 · 公众号/短视频文案", "tag": "独立"},
            {"title": "赵敏 · 数据分析", "meta": "2年经验 · Python/SQL", "tag": "独立"},
            {"title": "陈晨 · 电商运营", "meta": "3年经验 · 抖音/淘宝", "tag": "认证"},
            {"title": "刘洋 · 空间设计", "meta": "6年经验 · 民宿/商业空间", "tag": "认证"},
        ],
    },
    ("carrier", "opc"): {
        "items": [
            {"title": "云创设计工作室", "meta": "电子信息方向 · 工位12 · 2026-03 入驻", "tag": "已入驻"},
            {"title": "滇峰数据科技", "meta": "软件服务方向 · 工位8 · 2026-04 入驻", "tag": "已入驻"},
            {"title": "云南新媒体中心", "meta": "内容创作方向 · 工位6 · 待审批", "tag": "待审批"},
            {"title": "绿野农业电商", "meta": "电商方向 · 工位10 · 2026-05 入驻", "tag": "已入驻"},
            {"title": "云南文创工坊", "meta": "文化创意方向 · 工位4 · 申请中", "tag": "申请中"},
        ],
    },
    ("carrier", "services"): {
        "items": [
            {"title": "园区创业培训", "meta": "创业辅导 · 月度课程 · 已完成", "tag": "已完成"},
            {"title": "工商注册代办", "meta": "云超财税 · 本期 12 单", "tag": "合作中"},
            {"title": "政策申报指导", "meta": "省市区三级补贴申报", "tag": "进行中"},
            {"title": "法务顾问服务", "meta": "滇峰法律 · 入驻企业专享", "tag": "合作中"},
        ],
    },
    ("provider", "orders"): {
        "items": [
            {"title": "代理记账服务（月度）", "meta": "云南某某甲方企业 · ¥199/月 · 2026-07 开始", "tag": "进行中"},
            {"title": "公司注册代办", "meta": "个人创业者 · ¥599/次 · 待处理", "tag": "待处理"},
            {"title": "季度财务报表编制", "meta": "云创财税 · ¥2000-3000 · 已完成", "tag": "已完成"},
            {"title": "税务筹划咨询", "meta": "鼎信税务所 · ¥1999/次 · 进行中", "tag": "进行中"},
            {"title": "年度汇算清缴", "meta": "云南某某甲方企业 · ¥1500 · 待处理", "tag": "待处理"},
        ],
    },
    ("provider", "services"): {
        "items": [
            {"title": "代理记账服务", "meta": "财税服务 · ¥199/月起 · 在售", "tag": "在售"},
            {"title": "税务筹划咨询", "meta": "财税服务 · ¥1999/次 · 在售", "tag": "在售"},
            {"title": "企业年报填报", "meta": "工商服务 · ¥99/次 · 在售", "tag": "在售"},
            {"title": "审计报告出具", "meta": "财税服务 · ¥3000/份 · 草稿", "tag": "草稿"},
        ],
    },
    ("government", "opc"): {
        "items": [
            {"title": "张伟 · UI设计师", "meta": "盘龙区 · 认证OPC · 信用88", "tag": "认证"},
            {"title": "李娜 · 前端开发", "meta": "五华区 · 认证OPC · 信用85", "tag": "认证"},
            {"title": "王强 · 文案策划", "meta": "盘龙区 · 独立OPC · 信用80", "tag": "独立"},
            {"title": "赵敏 · 数据分析", "meta": "麒麟区 · 独立OPC · 信用82", "tag": "独立"},
        ],
    },
    ("government", "policies"): {
        "items": [
            {"title": "云南省一次性创业补贴政策", "meta": "省级 · 2026-06 发布 · 申报中", "tag": "已发布"},
            {"title": "昆明市创业担保贷款政策", "meta": "市级 · 2026-05 发布 · 申报中", "tag": "已发布"},
            {"title": "小微企业税收优惠减免", "meta": "省级 · 2026-04 发布 · 申报中", "tag": "已发布"},
            {"title": "青年人才创业支持计划", "meta": "市级 · 起草中", "tag": "草稿"},
        ],
    },
    ("government", "carriers"): {
        "items": [
            {"title": "滇池创业园", "meta": "五华区 · 工位500 · 入驻率84%", "tag": "正常"},
            {"title": "云创空间孵化器", "meta": "盘龙区 · 工位300 · 入驻率76%", "tag": "正常"},
            {"title": "曲靖高新园区", "meta": "麒麟区 · 工位400 · 入驻率68%", "tag": "正常"},
        ],
    },
    ("government", "data"): {
        "stats": [
            {"key": "opcTotal", "value": 45600},
            {"key": "taskTotal", "value": 12500},
            {"key": "carrierTotal", "value": 168},
            {"key": "policyTotal", "value": 245},
        ],
        "chart": [
            {"label": "1月", "value": 3200},
            {"label": "2月", "value": 3600},
            {"label": "3月", "value": 4100},
            {"label": "4月", "value": 3900},
            {"label": "5月", "value": 4500},
            {"label": "6月", "value": 5200},
        ],
    },
    ("investor", "projects"): {
        "items": [
            {"title": "云上数仓 SaaS 平台", "meta": "人工智能 · 天使轮 · 融资 300万 · 估值 2000万", "tag": "天使轮"},
            {"title": "滇南文旅数字营销", "meta": "文旅创意 · A轮 · 融资 800万 · 估值 6000万", "tag": "A轮"},
            {"title": "生物医药检测服务", "meta": "生物医药 · 种子轮 · 融资 150万", "tag": "种子轮"},
            {"title": "智慧农业物联", "meta": "农业科技 · 天使轮 · 融资 200万 · 估值 1500万", "tag": "天使轮"},
            {"title": "本地生活 AI 助手", "meta": "人工智能 · 种子轮 · 融资 100万", "tag": "种子轮"},
        ],
    },
    ("investor", "trainings"): {
        "items": [
            {"title": "商业计划书（BP）撰写实战", "meta": "投融资基础 · 视频课 · 免费", "tag": "免费"},
            {"title": "创业公司估值与投资条款", "meta": "投融资进阶 · 视频课 · 付费", "tag": "付费"},
            {"title": "尽职调查实务（投资人视角）", "meta": "投融资进阶 · 直播课 · 报名中", "tag": "报名中"},
            {"title": "路演技巧与打动投资人", "meta": "融资准备 · 视频课 · 免费", "tag": "免费"},
            {"title": "股权结构与法律合规", "meta": "融资准备 · 视频课 · 付费", "tag": "付费"},
        ],
    },
    ("investor", "portfolio"): {
        "stats": [
            {"key": "totalInvested", "value": "¥500万"},
            {"key": "portfolioCount", "value": 6},
            {"key": "activeIntents", "value": 3},
            {"key": "roadshowAttended", "value": 12},
        ],
        "items": [
            {"title": "云上数仓 SaaS", "meta": "已投 · 天使轮 · 当前估值 2000万", "tag": "已投"},
            {"title": "滇南文旅", "meta": "已投 · A轮 · 当前估值 6000万", "tag": "已投"},
            {"title": "本地生活 AI 助手", "meta": "跟踪中 · 种子轮", "tag": "跟踪中"},
        ],
    },
    ("opc", "tax"): {
        "items": [
            {"title": "个人所得税 · 7月申报", "meta": "申报期至 2026-08-15 · 待申报", "tag": "待申报"},
            {"title": "增值税 · 7月申报", "meta": "申报期至 2026-08-15 · 待申报", "tag": "待申报"},
            {"title": "企业所得税 · 二季度", "meta": "已于 2026-07-20 申报", "tag": "已申报"},
        ],
    },
    ("opc", "affairs"): {
        "items": [
            {"title": "个体工商户注册", "meta": "工商办理 · 材料已提交 · 办理中", "tag": "办理中"},
            {"title": "年报公示", "meta": "工商办理 · 已完成", "tag": "已完成"},
            {"title": "一次性创业补贴申报", "meta": "政务申报 · 区县初审中", "tag": "初审中"},
        ],
    },
    ("developer", "dashboard"): {
        "stats": [
            {"key": "appCount", "value": 3},
            {"key": "pluginCount", "value": 5},
            {"key": "skillCount", "value": 8},
            {"key": "apiCalls", "value": "12.6万"},
        ],
    },
    ("developer", "apps"): {
        "items": [
            {"title": "云上订单助手", "meta": "应用 · 已上架 · 调用 3.2万次", "tag": "已上架"},
            {"title": "创业政策查询", "meta": "应用 · 已上架 · 调用 5.1万次", "tag": "已上架"},
            {"title": "财税自动记账", "meta": "应用 · 待审核", "tag": "待审核"},
        ],
    },
    ("developer", "plugins"): {
        "items": [
            {"title": "task-helper", "meta": "插件 · 版本 v1.2 · 已上架", "tag": "已上架"},
            {"title": "policy-mcp", "meta": "插件 · MCP · 已上架", "tag": "已上架"},
            {"title": "invoice-extract", "meta": "插件 · 开发中", "tag": "开发中"},
        ],
    },
    ("developer", "skills"): {
        "items": [
            {"title": "BP 撰写助手", "meta": "技能 · 已上架 · 使用 1200次", "tag": "已上架"},
            {"title": "财税问答", "meta": "技能 · 已上架 · 使用 860次", "tag": "已上架"},
            {"title": "路演 PPT 生成", "meta": "技能 · 待审核", "tag": "待审核"},
        ],
    },
}


async def _seed_port_pages(session: AsyncSession, now: str) -> None:
    """写入端口子页面演示数据（幂等：任一 (port,page) 已存在则跳过）。"""
    if await session.scalar(select(PortalPage.port).limit(1)) is not None:
        return
    session.add_all(
        [
            PortalPage(
                port=port, page=page,
                payload=json.dumps(payload, ensure_ascii=False),
                updated_at=now,
            )
            for (port, page), payload in _PORT_PAGES.items()
        ]
    )


async def _seed_investor(session: AsyncSession, now: str) -> None:
    """投资人端演示数据（偏好/路演/意向），幂等（以偏好为标记）。"""
    if await session.scalar(select(InvestorPreference.user_id).limit(1)) is not None:
        return
    session.add_all([
        InvestorPreference(
            user_id="u_inv_01",
            industries='["人工智能","文旅","农业科技"]',
            stage="angel", amount_min=50, amount_max=300, region_id="r_prov_yn",
            updated_at=now,
        ),
        InvestorPreference(
            user_id="u_inv_02",
            industries='["人工智能","生物医药","智能制造"]',
            stage="series_a", amount_min=500, amount_max=3000, region_id="r_prov_yn",
            updated_at=now,
        ),
    ])
    session.add_all([
        Roadshow(
            id="rs_001", publisher_id="u_inv_02", publisher_role="investor",
            title="云南 AI 创业项目专场路演", summary="8 个 AI 方向创业项目线上路演",
            activity_type="online", scope_type="all", region_id=None,
            start_at="2026-08-20T14:00:00", end_at="2026-08-20T17:00:00",
            register_deadline="2026-08-18", quota=100, live_url="",
            status="published", need_review=True, created_at=now, updated_at=now,
        ),
        Roadshow(
            id="rs_002", publisher_id="u_gov_prov", publisher_role="government",
            title="省级创业投融资对接会", summary="省级载体与投资人对接",
            activity_type="hybrid", scope_type="region", region_id="r_prov_yn",
            start_at="2026-08-28T09:00:00", end_at="2026-08-28T12:00:00",
            register_deadline="2026-08-25", quota=200, venue="昆明国际会展中心",
            status="published", need_review=False, created_at=now, updated_at=now,
        ),
    ])
    session.add_all([
        InvestmentIntent(
            id="intent_001", investor_id="u_inv_01", project_id="proj_001",
            project_name="云上数仓 SaaS 平台", status="contacted",
            message="对贵项目感兴趣，希望约谈", created_at=now, updated_at=now,
        ),
    ])


async def _sync_permissions(session: AsyncSession, now: str) -> None:
    """幂等补种权限与角色权限映射（供既有库使用）。

    只插入缺失的 Permission 行与缺失的 RolePermission 链接，不删除已有
    自定义授权，避免覆盖运营端后续经「角色权限配置」做的调整。
    """
    for pid, name, category, module in PERMISSIONS:
        if await session.get(Permission, pid) is None:
            session.add(Permission(id=pid, name=name, category=category, module=module))
    for role_id, perms in ROLE_PERMISSIONS.items():
        if await session.get(Role, role_id) is None:
            continue
        existing = {
            rp.permission_id
            for rp in await session.scalars(
                select(RolePermission).where(RolePermission.role_id == role_id)
            )
        }
        for perm in perms:
            if perm not in existing:
                session.add(RolePermission(role_id=role_id, permission_id=perm))


async def _ensure_extra_demo_users(session: AsyncSession, now: str) -> None:
    """补建新增的演示用户（投资人等），供既有库使用（幂等）。"""
    EXTRA = [
        ("u_inv_01", "inv01", "投资人账号", "investor", "personal", None, "r_prov_yn"),
        ("u_inv_02", "inv02", "机构投资人", "investor", "org_director", None, "r_prov_yn"),
        # 运营端内部子角色演示账号
        ("u_op_admin", "op_admin", "运营管理员", "operator", "op_admin", None, "r_prov_yn"),
        ("u_op_cs", "op_cs", "运营客服", "operator", "op_customer_service", None, "r_prov_yn"),
        ("u_op_fin", "op_fin", "运营财务", "operator", "op_finance", None, "r_prov_yn"),
        ("u_op_tech", "op_tech", "运营运维", "operator", "op_techops", None, "r_prov_yn"),
        # 机构子账号（企业财务/任务发布、服务商执行/财务）
        ("u_ent_fin", "ent_fin", "企业财务", "enterprise", "operator", "o_ent_001", "r_dist_pl"),
        ("u_ent_pub", "ent_pub", "企业任务发布", "enterprise", "operator", "o_ent_001", "r_dist_pl"),
        ("u_pro_exec", "pro_exec", "服务商执行", "provider", "operator", "o_pro_001", "r_city_km"),
        ("u_pro_fin", "pro_fin", "服务商财务", "provider", "operator", "o_pro_001", "r_city_km"),
        # 开放平台开发者
        ("u_dev_01", "dev01", "个人开发者", "developer", "dev_individual", None, "r_prov_yn"),
        ("u_dev_02", "dev02", "机构开发者", "developer", "dev_org_admin", None, "r_prov_yn"),
    ]
    for (uid, username, nickname, role, sub_role, org_id, region_id) in EXTRA:
        if await session.get(User, uid) is not None:
            continue
        digest, salt = hash_password("123456")
        session.add(
            User(
                id=uid, username=username, nickname=nickname,
                password_hash=digest, password_salt=salt,
                role=role, sub_role=sub_role, org_id=org_id, region_id=region_id,
                status="active", token_version=0, created_at=now, updated_at=now,
            )
        )
        for seed in AGENT_SEED:
            await _add_agent(session, seed, uid, None, now)


async def _seed_ecosystem(session: AsyncSession, now: str) -> None:
    """生态横切演示数据（通知/补贴/引荐/培训），幂等。"""
    if await session.scalar(select(Notification.id).limit(1)) is None:
        session.add_all([
            Notification(id="ntf_001", user_id="u_opc_01", type="task",
                         title="任务进度更新", content="您的任务「电商小程序首页 UI 设计」进度已更新",
                         read=False, created_at=now),
            Notification(id="ntf_002", user_id="u_opc_01", type="policy",
                         title="新政策匹配", content="2 项新政策与您高度匹配", read=False, created_at=now),
            Notification(id="ntf_003", user_id="u_ent_01", type="task",
                         title="收到投标", content="您的任务收到新的竞标", read=False, created_at=now),
        ])
    if await session.scalar(select(SubsidyApplication.id).limit(1)) is not None:
        return
    session.add_all([
        SubsidyApplication(id="sub_001", opc_id="u_opc_01", opc_name="OPC个人创业者",
                           title="云南省一次性创业补贴", amount=10000, region_id="r_dist_wh",
                           status="applying", created_at=now, updated_at=now),
        SubsidyApplication(id="sub_002", opc_id="u_opc_02", opc_name="OPC个体工商户",
                           title="昆明市创业担保贷款贴息", amount=5000, region_id="r_dist_pl",
                           status="district_review", created_at=now, updated_at=now),
        SubsidyApplication(id="sub_003", opc_id="u_opc_01", opc_name="OPC个人创业者",
                           title="青年人才创业支持计划", amount=20000, region_id="r_dist_wh",
                           status="city_review", created_at=now, updated_at=now),
    ])
    session.add_all([
        ServiceReferral(id="ref_001", carrier_id="o_car_001", provider_id="o_pro_001",
                        provider_name="云超财税服务所", opc_id="u_opc_01", opc_name="OPC个人创业者",
                        status="referred", created_at=now, updated_at=now),
    ])
    session.add_all([
        TrainingEnrollment(id="treg_001", user_id="u_inv_01", training_id="商业计划书（BP）撰写实战",
                           training_name="商业计划书（BP）撰写实战", status="enrolled", created_at=now),
    ])


async def _ensure_port_agents(session: AsyncSession, now: str) -> None:
    """为每个账号的每个端口身份确保默认/QA 智能体（多端口彻底隔离）。"""
    await session.flush()  # 确保此前新增的身份可见
    rows = (await session.execute(select(UserIdentity.user_id, UserIdentity.port))).all()
    seen: set[tuple[str, str]] = set()
    for user_id, port in rows:
        if (user_id, port) in seen:
            continue
        seen.add((user_id, port))
        existing_ids = set(
            (await session.execute(select(Agent.id).where(Agent.user_id == user_id, Agent.port == port))).scalars()
        )
        for seed in AGENT_SEED:
            await _add_agent(session, seed, user_id, port, now)


async def _seed_org_members(session: AsyncSession, now: str) -> None:
    """机构成员（机构主账号 + 子账号），幂等（以 organization_members 为标记）。"""
    await session.flush()
    if await session.scalar(select(OrganizationMember.org_id).limit(1)) is not None:
        return
    # 每个机构默认一个管理员主账号
    members = [
        ("o_ent_001", "u_ent_01", "admin", True),
        ("o_car_001", "u_car_01", "admin", True),
        ("o_pro_001", "u_pro_01", "admin", True),
        # 企业子账号（财务/任务发布）
        ("o_ent_001", "u_ent_fin", "finance", False),
        ("o_ent_001", "u_ent_pub", "publisher", False),
        # 服务商子账号（服务执行/财务）
        ("o_pro_001", "u_pro_exec", "service_executor", False),
        ("o_pro_001", "u_pro_fin", "finance", False),
    ]
    for org_id, user_id, role, is_admin in members:
        session.add(OrganizationMember(org_id=org_id, user_id=user_id, role=role,
                                       is_admin=is_admin, status="active", joined_at=now))
