from fastapi import BackgroundTasks, UploadFile
from utils.response_builder import ResponseBuilder
from config.settings import settings
from services.abnormal_account_baseline_service import AbnormalAccountBaselineService
from services.phase2_baseline_service import Phase2BaselineService
from services.staff_identity_repository import StaffIdentityRepository
from typing import Dict, List, Union
from dataclasses import dataclass, field
import time
from datetime import datetime, timedelta
import random
import uuid

LARGE_TRANSACTION_RULE_CODES = [
    "HOUSE_OR_CAR_EXPENSE",
    "TAX_EXPENSE",
    "SINGLE_LARGE_INCOME",
    "CUMULATIVE_INCOME",
    "ANNUAL_TURNOVER",
    "LARGE_CASH_DEPOSIT",
    "FREQUENT_CASH_DEPOSIT",
    "LARGE_TRANSFER",
]

PHASE1_RULE_CODES = [
    "GAMBLING_SENSITIVE_KEYWORD",
    "SPECIAL_AMOUNT_TRANSACTION",
    "SUSPICIOUS_INCOME_KEYWORD",
    "FOREX_BUY_AMT",
    "FOREX_SELL_AMT",
    "STOCK_TFR_LARGE",
    "LARGE_STOCK_TRADING",
    "WITHDRAW_CNT",
]

PHASE2_STATEMENT_RULE_CODES = [
    "LOW_INCOME_RELATIVE_LARGE_TRANSACTION",
    "MULTI_PARTY_GAMBLING_TRANSFER",
    "MONTHLY_FIXED_INCOME",
    "FIXED_COUNTERPARTY_TRANSFER",
    "SALARY_QUICK_TRANSFER",
    "SALARY_UNUSED",
]

PHASE2_BASELINE_RULE_CODES = [
    "HOUSE_REGISTRATION_MISMATCH",
    "PROPERTY_FEE_REGISTRATION_MISMATCH",
    "TAX_ASSET_REGISTRATION_MISMATCH",
    "SUPPLIER_CONCENTRATION",
]

ABNORMAL_ACCOUNT_RULE_CODES = [
    "SUDDEN_ACCOUNT_CLOSURE",
    "DORMANT_ACCOUNT_LARGE_ACTIVATION",
]

MONTHLY_FIXED_INCOME_ISOLATED_LARGE_TRANSACTION_RULE_CODES = {
    "SINGLE_LARGE_INCOME",
    "CUMULATIVE_INCOME",
    "ANNUAL_TURNOVER",
    "LARGE_CASH_DEPOSIT",
    "FREQUENT_CASH_DEPOSIT",
}
MONTHLY_FIXED_INCOME_ISOLATED_PHASE1_RULE_CODES = {
    "SUSPICIOUS_INCOME_KEYWORD",
    "FOREX_SELL_AMT",
}
MONTHLY_FIXED_INCOME_ISOLATED_PHASE2_RULE_CODES = {
    "FIXED_COUNTERPARTY_TRANSFER",
}

RULE_CONFLICT_GROUPS = []
ALL_MODE_STATEMENT_BASELINE_RULE_CODES = {
    "LOW_INCOME_RELATIVE_LARGE_TRANSACTION",
}


@dataclass
class FileRecord:
    """文件记录模型（扩展版）"""
    # 原有字段
    log_id: int
    group_id: int
    file_name: str
    status: int = -5  # -5 表示解析成功
    upload_status_desc: str = "data.wait.confirm.newaccount"
    parsing: bool = True  # True表示正在解析

    # 新增字段 - 账号和主体信息
    primary_enterprise_name: str = ""
    primary_account_no: str = ""
    account_no_list: List[str] = field(default_factory=list)
    enterprise_name_list: List[str] = field(default_factory=list)

    # 新增字段 - 银行和模板信息
    bank_name: str = "ZJRCU"
    real_bank_name: str = "ZJRCU"
    template_name: str = "ZJRCU_T251114"
    data_type_info: List[str] = field(default_factory=lambda: ["CSV", ","])

    # 新增字段 - 文件元数据
    file_size: int = 50000
    download_file_name: str = ""
    file_package_id: str = field(default_factory=lambda: str(uuid.uuid4()).replace('-', ''))

    # 新增字段 - 上传用户信息
    file_upload_by: int = 448
    file_upload_by_user_name: str = "admin@support.com"
    file_upload_time: str = field(default_factory=lambda: datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

    # 新增字段 - 法律实体信息
    le_id: int = 10000
    login_le_id: int = 10000
    log_type: str = "bankstatement"
    log_meta: str = "{\"lostHeader\":[],\"balanceAmount\":\"-1\"}"
    lost_header: List[str] = field(default_factory=list)

    # 新增字段 - 记录统计
    rows: int = 0
    source: str = "http"
    total_records: int = 150
    is_split: int = 0

    # 新增字段 - 交易日期范围
    trx_date_start_id: int = 20240101
    trx_date_end_id: int = 20241231

    # 新增字段 - 身份绑定
    staff_name: str = ""
    staff_id_card: str = ""
    family_id_cards: List[str] = field(default_factory=list)
    large_transaction_hit_rules: List[str] = field(default_factory=list)
    phase1_hit_rules: List[str] = field(default_factory=list)
    phase2_statement_hit_rules: List[str] = field(default_factory=list)
    phase2_baseline_hit_rules: List[str] = field(default_factory=list)
    abnormal_account_hit_rules: List[str] = field(default_factory=list)
    abnormal_accounts: List[dict] = field(default_factory=list)

class FileService:
    """文件上传和解析服务"""

    INNER_FLOW_TOTAL_RECORDS = 200
    LOG_ID_MIN = settings.INITIAL_LOG_ID
    LOG_ID_MAX = 99999

    def __init__(
        self,
        staff_identity_repository=None,
        phase2_baseline_service=None,
        abnormal_account_baseline_service=None,
    ):
        self.file_records: Dict[int, FileRecord] = {}  # logId -> FileRecord
        self.log_counter = settings.INITIAL_LOG_ID
        self.staff_identity_repository = staff_identity_repository or StaffIdentityRepository()
        self.phase2_baseline_service = phase2_baseline_service or Phase2BaselineService()
        self.abnormal_account_baseline_service = (
            abnormal_account_baseline_service or AbnormalAccountBaselineService()
        )

    def get_file_record(self, log_id: int) -> FileRecord:
        """按 logId 获取已存在的文件记录。"""
        return self.file_records.get(log_id)

    def _generate_log_id(self) -> int:
        """生成当前进程内未占用的随机 logId。"""
        available_capacity = self.LOG_ID_MAX - self.LOG_ID_MIN + 1
        if len(self.file_records) >= available_capacity:
            raise RuntimeError("可用 logId 已耗尽")

        while True:
            candidate = random.randint(self.LOG_ID_MIN, self.LOG_ID_MAX)
            if candidate not in self.file_records:
                self.log_counter = candidate
                return candidate

    def _infer_bank_name(self, filename: str) -> tuple:
        """根据文件名推断银行名称和模板名称"""
        if "支付宝" in filename or "alipay" in filename.lower():
            return "ALIPAY", "ALIPAY_T220708"
        elif "绍兴银行" in filename or "BSX" in filename:
            return "BSX", "BSX_T240925"
        else:
            return "ZJRCU", "ZJRCU_T251114"

    def _generate_primary_binding(self) -> tuple:
        """生成单一稳定的本方主体/本方账号绑定。"""
        primary_account_no = f"{random.randint(10000000000, 99999999999)}"
        primary_enterprise_name = "测试主体"
        return primary_enterprise_name, primary_account_no

    def _generate_primary_binding_from_rng(self, rng: random.Random) -> tuple:
        """使用局部随机源生成单一稳定的本方主体/本方账号绑定。"""
        primary_account_no = f"{rng.randint(10000000000, 99999999999)}"
        primary_enterprise_name = "测试主体"
        return primary_enterprise_name, primary_account_no

    def _build_primary_binding_lists(
        self, primary_enterprise_name: str, primary_account_no: str
    ) -> dict:
        """基于主绑定事实源构建列表字段。"""
        return {
            "accountNoList": [primary_account_no],
            "enterpriseNameList": [primary_enterprise_name],
        }

    def _pick_rule_subset(
        self,
        rng: random.Random,
        rule_codes: List[str],
        min_count: int,
        max_count: int,
    ) -> List[str]:
        """按固定随机源选择稳定规则子集，并保留规则池原始顺序。"""
        selected_codes = set(rng.sample(rule_codes, rng.randint(min_count, max_count)))
        return [rule_code for rule_code in rule_codes if rule_code in selected_codes]

    def _build_subset_rule_hit_plan(self, log_id: int) -> dict:
        """基于 logId 生成稳定的规则子集命中计划。"""
        rng = random.Random(f"rule-plan:{log_id}")
        return {
            "large_transaction_hit_rules": self._pick_rule_subset(
                rng, LARGE_TRANSACTION_RULE_CODES, 2, 4
            ),
            "phase1_hit_rules": self._pick_rule_subset(rng, PHASE1_RULE_CODES, 2, 4),
            "phase2_statement_hit_rules": self._pick_rule_subset(
                rng, PHASE2_STATEMENT_RULE_CODES, 2, 4
            ),
            "phase2_baseline_hit_rules": self._pick_rule_subset(
                rng, PHASE2_BASELINE_RULE_CODES, 2, 4
            ),
            "abnormal_account_hit_rules": self._pick_rule_subset(
                rng, ABNORMAL_ACCOUNT_RULE_CODES, 1, len(ABNORMAL_ACCOUNT_RULE_CODES)
            ),
        }

    def _build_all_compatible_rule_hit_plan(self) -> dict:
        """生成全部兼容规则命中计划。"""
        return {
            "large_transaction_hit_rules": list(LARGE_TRANSACTION_RULE_CODES),
            "phase1_hit_rules": list(PHASE1_RULE_CODES),
            "phase2_statement_hit_rules": list(PHASE2_STATEMENT_RULE_CODES),
            "phase2_baseline_hit_rules": list(PHASE2_BASELINE_RULE_CODES),
            "abnormal_account_hit_rules": list(ABNORMAL_ACCOUNT_RULE_CODES),
        }

    def _build_monthly_fixed_income_isolated_rule_hit_plan(self) -> dict:
        """为月固定收入准备不受正向流入污染的 all 模式计划。"""
        full_plan = self._build_all_compatible_rule_hit_plan()
        return {
            "large_transaction_hit_rules": [
                rule_code
                for rule_code in full_plan["large_transaction_hit_rules"]
                if rule_code not in MONTHLY_FIXED_INCOME_ISOLATED_LARGE_TRANSACTION_RULE_CODES
            ],
            "phase1_hit_rules": [
                rule_code
                for rule_code in full_plan["phase1_hit_rules"]
                if rule_code not in MONTHLY_FIXED_INCOME_ISOLATED_PHASE1_RULE_CODES
            ],
            "phase2_statement_hit_rules": [
                rule_code
                for rule_code in full_plan["phase2_statement_hit_rules"]
                if rule_code not in MONTHLY_FIXED_INCOME_ISOLATED_PHASE2_RULE_CODES
            ],
            "phase2_baseline_hit_rules": list(full_plan["phase2_baseline_hit_rules"]),
        }

    def _apply_conflict_groups(self, rule_plan: dict) -> dict:
        """按显式互斥组裁剪规则计划，同组仅保留固定优先级的首个规则。"""
        resolved_plan = {plan_key: list(rule_codes) for plan_key, rule_codes in rule_plan.items()}
        for plan_key, rule_codes in resolved_plan.items():
            filtered_codes = list(rule_codes)
            for conflict_group in RULE_CONFLICT_GROUPS:
                kept_rule_code = next(
                    (rule_code for rule_code in conflict_group if rule_code in filtered_codes),
                    None,
                )
                if kept_rule_code is None:
                    continue
                filtered_codes = [
                    rule_code
                    for rule_code in filtered_codes
                    if rule_code == kept_rule_code or rule_code not in conflict_group
                ]
            resolved_plan[plan_key] = filtered_codes
        return resolved_plan

    def _build_rule_hit_plan(self, log_id: int) -> dict:
        """按配置模式生成规则命中计划。"""
        if settings.RULE_HIT_MODE == "all":
            return self._apply_conflict_groups(self._build_all_compatible_rule_hit_plan())
        return self._build_subset_rule_hit_plan(log_id)

    def _apply_rule_hit_plan_to_record(self, file_record: FileRecord, rule_hit_plan: dict) -> None:
        """将规则命中计划回填到指定文件记录。"""
        file_record.large_transaction_hit_rules = list(
            rule_hit_plan.get("large_transaction_hit_rules", [])
        )
        file_record.phase1_hit_rules = list(rule_hit_plan.get("phase1_hit_rules", []))
        file_record.phase2_statement_hit_rules = list(
            rule_hit_plan.get("phase2_statement_hit_rules", [])
        )
        file_record.phase2_baseline_hit_rules = list(
            rule_hit_plan.get("phase2_baseline_hit_rules", [])
        )
        file_record.abnormal_account_hit_rules = list(
            rule_hit_plan.get("abnormal_account_hit_rules", [])
        )
        file_record.abnormal_accounts = self._build_abnormal_accounts(
            log_id=file_record.log_id,
            staff_id_card=file_record.staff_id_card,
            abnormal_account_hit_rules=file_record.abnormal_account_hit_rules,
        )

    def _build_abnormal_accounts(
        self,
        *,
        log_id: int,
        staff_id_card: str,
        abnormal_account_hit_rules: List[str],
    ) -> List[dict]:
        """按命中规则生成最小异常账户事实。"""
        if not abnormal_account_hit_rules:
            return []

        rng = random.Random(f"abnormal-account:{log_id}")
        accounts = []
        for index, rule_code in enumerate(abnormal_account_hit_rules, start=1):
            account_no = f"622200{rng.randint(10**9, 10**10 - 1)}"
            account_fact = {
                "account_no": account_no,
                "owner_id_card": staff_id_card,
                "account_name": "测试员工工资卡",
                "status": 1,
                "effective_date": "2025-01-01",
                "invalid_date": None,
                "rule_code": rule_code,
            }
            if rule_code == "SUDDEN_ACCOUNT_CLOSURE":
                account_fact["status"] = 2
                account_fact["effective_date"] = "2024-01-01"
                account_fact["invalid_date"] = "2026-03-20"
            elif rule_code == "DORMANT_ACCOUNT_LARGE_ACTIVATION":
                account_fact["status"] = 1
                account_fact["effective_date"] = "2025-01-01"
                account_fact["invalid_date"] = None

            account_fact["account_no"] = f"{account_no[:-2]}{index:02d}"
            accounts.append(account_fact)

        return accounts

    def _rebalance_all_mode_group_rule_plans(self, group_id: int) -> None:
        """同项目存在多文件时，隔离月固定收入样本，避免被其他正向流入规则污染。"""
        if settings.RULE_HIT_MODE != "all":
            return

        group_records = [
            record for record in self.file_records.values()
            if record.group_id == group_id
        ]
        if not group_records:
            return

        full_plan = self._apply_conflict_groups(self._build_all_compatible_rule_hit_plan())
        if len(group_records) == 1:
            return

        monthly_safe_plan = self._apply_conflict_groups(
            self._build_monthly_fixed_income_isolated_rule_hit_plan()
        )
        self._apply_rule_hit_plan_to_record(group_records[0], monthly_safe_plan)
        for record in group_records[1:]:
            self._apply_rule_hit_plan_to_record(record, full_plan)

    def _create_file_record(
        self,
        *,
        log_id: int,
        group_id: int,
        file_name: str,
        download_file_name: str,
        bank_name: str,
        template_name: str,
        primary_enterprise_name: str,
        primary_account_no: str,
        file_size: int,
        total_records: int,
        trx_date_start_id: int,
        trx_date_end_id: int,
        le_id: int,
        login_le_id: int,
        staff_name: str = "",
        staff_id_card: str = "",
        family_id_cards: List[str] = None,
        large_transaction_hit_rules: List[str] = None,
        phase1_hit_rules: List[str] = None,
        phase2_statement_hit_rules: List[str] = None,
        phase2_baseline_hit_rules: List[str] = None,
        abnormal_account_hit_rules: List[str] = None,
        abnormal_accounts: List[dict] = None,
        parsing: bool = True,
        status: int = -5,
    ) -> FileRecord:
        """创建文件记录并写入主绑定信息。"""
        binding_lists = self._build_primary_binding_lists(
            primary_enterprise_name,
            primary_account_no,
        )

        return FileRecord(
            log_id=log_id,
            group_id=group_id,
            file_name=file_name,
            download_file_name=download_file_name,
            bank_name=bank_name,
            real_bank_name=bank_name,
            template_name=template_name,
            primary_enterprise_name=primary_enterprise_name,
            primary_account_no=primary_account_no,
            account_no_list=binding_lists["accountNoList"],
            enterprise_name_list=binding_lists["enterpriseNameList"],
            le_id=le_id,
            login_le_id=login_le_id,
            file_size=file_size,
            total_records=total_records,
            trx_date_start_id=trx_date_start_id,
            trx_date_end_id=trx_date_end_id,
            staff_name=staff_name,
            staff_id_card=staff_id_card,
            family_id_cards=list(family_id_cards or []),
            large_transaction_hit_rules=list(large_transaction_hit_rules or []),
            phase1_hit_rules=list(phase1_hit_rules or []),
            phase2_statement_hit_rules=list(phase2_statement_hit_rules or []),
            phase2_baseline_hit_rules=list(phase2_baseline_hit_rules or []),
            abnormal_account_hit_rules=list(abnormal_account_hit_rules or []),
            abnormal_accounts=[dict(account) for account in (abnormal_accounts or [])],
            parsing=parsing,
            status=status,
        )

    def _select_staff_identity_scope(self) -> dict:
        """读取一个员工及其亲属身份范围。"""
        return self.staff_identity_repository.select_random_staff_with_families()

    def _apply_phase2_baselines(self, file_record: FileRecord) -> None:
        """按当前记录命中的第二期基线规则幂等补齐外部事实。"""
        baseline_rule_codes = list(file_record.phase2_baseline_hit_rules)
        if settings.RULE_HIT_MODE == "all":
            for rule_code in file_record.phase2_statement_hit_rules:
                if (
                    rule_code in ALL_MODE_STATEMENT_BASELINE_RULE_CODES
                    and rule_code not in baseline_rule_codes
                ):
                    baseline_rule_codes.append(rule_code)

        self.phase2_baseline_service.apply(
            staff_id_card=file_record.staff_id_card,
            family_id_cards=file_record.family_id_cards,
            baseline_rule_codes=baseline_rule_codes,
        )

    def _apply_abnormal_account_baselines(self, file_record: FileRecord) -> None:
        """按当前记录命中的异常账户规则幂等补齐账户事实。"""
        if not file_record.abnormal_account_hit_rules:
            return
        if not file_record.abnormal_accounts:
            raise RuntimeError("异常账户命中计划存在，但未生成账户事实")
        self.abnormal_account_baseline_service.apply(
            staff_id_card=file_record.staff_id_card,
            abnormal_accounts=file_record.abnormal_accounts,
        )

    async def upload_file(
        self, group_id: int, file: UploadFile, background_tasks: BackgroundTasks
    ) -> Dict:
        """上传文件并启动后台解析任务

        Args:
            group_id: 项目ID
            file: 上传的文件
            background_tasks: FastAPI后台任务

        Returns:
            上传响应字典
        """
        # 生成唯一 logId
        log_id = self._generate_log_id()

        # 推断银行信息
        bank_name, template_name = self._infer_bank_name(file.filename)
        rule_hit_plan = self._build_rule_hit_plan(log_id)

        # 生成合理的交易日期范围
        end_date = datetime.now()
        start_date = end_date - timedelta(days=random.randint(90, 365))
        trx_date_start_id = int(start_date.strftime("%Y%m%d"))
        trx_date_end_id = int(end_date.strftime("%Y%m%d"))

        # 生成单一主绑定
        primary_enterprise_name, primary_account_no = self._generate_primary_binding()
        identity_scope = self._select_staff_identity_scope()

        # 创建完整的文件记录
        file_record = self._create_file_record(
            log_id=log_id,
            group_id=group_id,
            file_name=file.filename,
            download_file_name=file.filename,
            bank_name=bank_name,
            template_name=template_name,
            primary_enterprise_name=primary_enterprise_name,
            primary_account_no=primary_account_no,
            file_size=random.randint(10000, 100000),
            total_records=random.randint(150, 200),
            trx_date_start_id=trx_date_start_id,
            trx_date_end_id=trx_date_end_id,
            le_id=10000 + random.randint(0, 9999),
            login_le_id=10000 + random.randint(0, 9999),
            staff_name=identity_scope["staff_name"],
            staff_id_card=identity_scope["staff_id_card"],
            family_id_cards=identity_scope["family_id_cards"],
            large_transaction_hit_rules=rule_hit_plan.get("large_transaction_hit_rules", []),
            phase1_hit_rules=rule_hit_plan.get("phase1_hit_rules", []),
            phase2_statement_hit_rules=rule_hit_plan.get("phase2_statement_hit_rules", []),
            phase2_baseline_hit_rules=rule_hit_plan.get("phase2_baseline_hit_rules", []),
            abnormal_account_hit_rules=rule_hit_plan.get("abnormal_account_hit_rules", []),
            abnormal_accounts=self._build_abnormal_accounts(
                log_id=log_id,
                staff_id_card=identity_scope["staff_id_card"],
                abnormal_account_hit_rules=rule_hit_plan.get("abnormal_account_hit_rules", []),
            ),
        )

        self._apply_abnormal_account_baselines(file_record)
        self.file_records[log_id] = file_record
        self._rebalance_all_mode_group_rule_plans(group_id)
        self._apply_phase2_baselines(file_record)

        # 添加后台任务（延迟解析）
        background_tasks.add_task(self._delayed_parse, log_id)

        # 构建响应
        return self._build_upload_response(file_record)

    def _build_upload_response(self, file_record: FileRecord) -> dict:
        """构建上传接口的完整响应"""
        return {
            "code": "200",
            "data": {
                "accountsOfLog": {
                    str(file_record.log_id): [
                        {
                            "bank": file_record.bank_name,
                            "accountName": file_record.primary_enterprise_name,
                            "accountNo": file_record.primary_account_no,
                            "currency": "CNY"
                        }
                    ]
                },
                "uploadLogList": [
                    {
                        **self._build_primary_binding_lists(
                            file_record.primary_enterprise_name,
                            file_record.primary_account_no,
                        ),
                        "bankName": file_record.bank_name,
                        "dataTypeInfo": file_record.data_type_info,
                        "downloadFileName": file_record.download_file_name,
                        "filePackageId": file_record.file_package_id,
                        "fileSize": file_record.file_size,
                        "fileUploadBy": file_record.file_upload_by,
                        "fileUploadByUserName": file_record.file_upload_by_user_name,
                        "fileUploadTime": file_record.file_upload_time,
                        "leId": file_record.le_id,
                        "logId": file_record.log_id,
                        "logMeta": file_record.log_meta,
                        "logType": file_record.log_type,
                        "loginLeId": file_record.login_le_id,
                        "lostHeader": file_record.lost_header,
                        "realBankName": file_record.real_bank_name,
                        "rows": file_record.rows,
                        "source": file_record.source,
                        "status": file_record.status,
                        "templateName": file_record.template_name,
                        "totalRecords": file_record.total_records,
                        "trxDateEndId": file_record.trx_date_end_id,
                        "trxDateStartId": file_record.trx_date_start_id,
                        "uploadFileName": file_record.file_name,
                        "uploadStatusDesc": file_record.upload_status_desc
                    }
                ],
                "uploadStatus": 1
            },
            "status": "200",
            "successResponse": True
        }

    def _delayed_parse(self, log_id: int):
        """后台任务：模拟文件解析过程

        Args:
            log_id: 日志ID
        """
        time.sleep(settings.PARSE_DELAY_SECONDS)

        # 解析完成，更新状态
        if log_id in self.file_records:
            self.file_records[log_id].parsing = False

    def _generate_deterministic_record(
        self, log_id: int, group_id: int, rng: random.Random
    ) -> dict:
        """
        基于 logId 生成确定性的文件记录

        Args:
            log_id: 文件ID（用作随机种子）
            group_id: 项目ID

        Returns:
            文件记录字典（26个字段）
        """
        # 银行类型选项
        bank_options = [
            ("ALIPAY", "ALIPAY_T220708"),
            ("BSX", "BSX_T240925"),
            ("ZJRCU", "ZJRCU_T251114")
        ]

        bank_name, template_name = rng.choice(bank_options)

        # 生成基于种子的稳定时间范围，确保同一 logId 重复查询完全一致
        base_datetime = datetime(2024, 1, 1, 8, 0, 0)
        end_date = base_datetime + timedelta(days=rng.randint(180, 540))
        start_date = end_date - timedelta(days=rng.randint(90, 365))
        file_upload_time = (
            base_datetime
            + timedelta(
                days=rng.randint(0, 540),
                hours=rng.randint(0, 23),
                minutes=rng.randint(0, 59),
                seconds=rng.randint(0, 59),
            )
        )

        # 生成账号和主体
        primary_enterprise_name, primary_account_no = self._generate_primary_binding_from_rng(rng)
        binding_lists = self._build_primary_binding_lists(
            primary_enterprise_name, primary_account_no
        )

        return {
            **binding_lists,
            "bankName": bank_name,
            "dataTypeInfo": ["CSV", ","],
            "downloadFileName": f"测试文件_{log_id}.csv",
            "fileSize": rng.randint(10000, 100000),
            "fileUploadBy": 448,
            "fileUploadByUserName": "admin@support.com",
            "fileUploadTime": file_upload_time.strftime("%Y-%m-%d %H:%M:%S"),
            "isSplit": 0,
            "leId": 10000 + rng.randint(0, 9999),
            "logId": log_id,
            "logMeta": "{\"lostHeader\":[],\"balanceAmount\":\"-1\"}",
            "logType": "bankstatement",
            "loginLeId": 10000 + rng.randint(0, 9999),
            "lostHeader": [],
            "realBankName": bank_name,
            "rows": 0,
            "source": "http",
            "status": -5,
            "templateName": template_name,
            "totalRecords": rng.randint(100, 300),
            "trxDateEndId": int(end_date.strftime("%Y%m%d")),
            "trxDateStartId": int(start_date.strftime("%Y%m%d")),
            "uploadFileName": f"测试文件_{log_id}.pdf",
            "uploadStatusDesc": "data.wait.confirm.newaccount"
        }

    def _build_deterministic_log_detail(self, log_id: int, group_id: int) -> dict:
        """构建 deterministic 回退的单条日志详情。"""
        rng = random.Random(log_id)
        return self._generate_deterministic_record(log_id, group_id, rng)

    def _build_log_detail(self, record: FileRecord) -> dict:
        """构建日志详情对象"""
        return {
            **self._build_primary_binding_lists(
                record.primary_enterprise_name,
                record.primary_account_no,
            ),
            "bankName": record.bank_name,
            "dataTypeInfo": record.data_type_info,
            "downloadFileName": record.download_file_name,
            "fileSize": record.file_size,
            "fileUploadBy": record.file_upload_by,
            "fileUploadByUserName": record.file_upload_by_user_name,
            "fileUploadTime": record.file_upload_time,
            "isSplit": record.is_split,
            "leId": record.le_id,
            "logId": record.log_id,
            "logMeta": record.log_meta,
            "logType": record.log_type,
            "loginLeId": record.login_le_id,
            "lostHeader": record.lost_header,
            "realBankName": record.real_bank_name,
            "rows": record.rows,
            "source": record.source,
            "status": record.status,
            "templateName": record.template_name,
            "totalRecords": record.total_records,
            "trxDateEndId": record.trx_date_end_id,
            "trxDateStartId": record.trx_date_start_id,
            "uploadFileName": record.file_name,
            "uploadStatusDesc": record.upload_status_desc
        }

    def check_parse_status(self, group_id: int, inprogress_list: str) -> Dict:
        """检查文件解析状态

        Args:
            group_id: 项目ID
            inprogress_list: 文件ID列表（逗号分隔）

        Returns:
            解析状态响应字典
        """
        # 解析logId列表
        log_ids = [int(x.strip()) for x in inprogress_list.split(",") if x.strip()]

        pending_list = []
        all_parsing_complete = True

        for log_id in log_ids:
            if log_id in self.file_records:
                record = self.file_records[log_id]
                if record.parsing:
                    all_parsing_complete = False

                pending_list.append(self._build_log_detail(record))

        return {
            "code": "200",
            "data": {
                "parsing": not all_parsing_complete,
                "pendingList": pending_list
            },
            "status": "200",
            "successResponse": True
        }

    def get_upload_status(self, group_id: int, log_id: int = None) -> dict:
        """
        获取文件上传状态（基于 logId 生成确定性数据）

        Args:
            group_id: 项目ID
            log_id: 文件ID（可选）

        Returns:
            上传状态响应字典
        """
        logs = []

        if log_id is not None:
            if log_id in self.file_records:
                log_detail = self._build_log_detail(self.file_records[log_id])
            else:
                log_detail = self._build_deterministic_log_detail(log_id, group_id)

            logs.append(log_detail)

        # 返回响应
        return {
            "code": "200",
            "data": {
                "logs": logs,
                "status": "",
                "accountId": 8954,
                "currency": "CNY"
            },
            "status": "200",
            "successResponse": True
        }

    def delete_files(self, group_id: int, log_ids: List[int], user_id: int) -> Dict:
        """删除文件

        Args:
            group_id: 项目ID
            log_ids: 文件ID列表
            user_id: 用户ID

        Returns:
            删除响应字典
        """
        # 删除文件记录
        deleted_count = 0
        for log_id in log_ids:
            if log_id in self.file_records:
                del self.file_records[log_id]
                deleted_count += 1

        return {
            "code": "200 OK",  # 注意：这里是 "200 OK" 不是 "200"
            "data": {
                "message": "delete.files.success"
            },
            "message": "delete.files.success",
            "status": "200",
            "successResponse": True
        }

    def fetch_inner_flow(self, request: Union[Dict, object]) -> Dict:
        """拉取行内流水（创建并保存绑定记录）

        Args:
            request: 拉取流水请求（可以是字典或对象）

        Returns:
            流水响应字典，包含创建并保存的logId数组
        """
        # 支持 dict 或对象
        if isinstance(request, dict):
            group_id = request.get("groupId", 1000)
            customer_no = request.get("customerNo", "")
            data_start_date_id = request.get("dataStartDateId", 20240101)
            data_end_date_id = request.get("dataEndDateId", 20241231)
        else:
            group_id = request.groupId
            customer_no = request.customerNo
            data_start_date_id = request.dataStartDateId
            data_end_date_id = request.dataEndDateId

        # 使用随机 logId，确保与上传链路一致且不覆盖现有记录
        log_id = self._generate_log_id()
        rule_hit_plan = self._build_rule_hit_plan(log_id)

        primary_enterprise_name, primary_account_no = self._generate_primary_binding()
        identity_scope = self._select_staff_identity_scope()
        file_record = self._create_file_record(
            log_id=log_id,
            group_id=group_id,
            file_name=f"{customer_no or 'inner_flow'}_{log_id}.csv",
            download_file_name=f"{customer_no or 'inner_flow'}_{log_id}.csv",
            bank_name="ZJRCU",
            template_name="ZJRCU_T251114",
            primary_enterprise_name=primary_enterprise_name,
            primary_account_no=primary_account_no,
            file_size=random.randint(10000, 100000),
            total_records=self.INNER_FLOW_TOTAL_RECORDS,
            trx_date_start_id=data_start_date_id,
            trx_date_end_id=data_end_date_id,
            le_id=10000 + random.randint(0, 9999),
            login_le_id=10000 + random.randint(0, 9999),
            staff_name=identity_scope["staff_name"],
            staff_id_card=identity_scope["staff_id_card"],
            family_id_cards=identity_scope["family_id_cards"],
            large_transaction_hit_rules=rule_hit_plan.get("large_transaction_hit_rules", []),
            phase1_hit_rules=rule_hit_plan.get("phase1_hit_rules", []),
            phase2_statement_hit_rules=rule_hit_plan.get("phase2_statement_hit_rules", []),
            phase2_baseline_hit_rules=rule_hit_plan.get("phase2_baseline_hit_rules", []),
            abnormal_account_hit_rules=rule_hit_plan.get("abnormal_account_hit_rules", []),
            abnormal_accounts=self._build_abnormal_accounts(
                log_id=log_id,
                staff_id_card=identity_scope["staff_id_card"],
                abnormal_account_hit_rules=rule_hit_plan.get("abnormal_account_hit_rules", []),
            ),
            parsing=False,
        )

        self._apply_abnormal_account_baselines(file_record)
        self.file_records[log_id] = file_record
        self._rebalance_all_mode_group_rule_plans(group_id)
        self._apply_phase2_baselines(file_record)

        # 返回成功的响应，包含logId数组
        return {
            "code": "200",
            "data": [log_id],
            "status": "200",
            "successResponse": True,
        }
