from typing import Optional, List, Tuple
from sqlalchemy.orm import Session
from fastapi import HTTPException, status

from app.models.classification import Category, DataLevel, RecognitionRule, ClassificationTemplate
from app.schemas.classification import CategoryCreate, CategoryUpdate, RecognitionRuleCreate, RecognitionRuleUpdate


def get_category(db: Session, category_id: int) -> Optional[Category]:
    return db.query(Category).filter(Category.id == category_id).first()


def list_categories(db: Session, parent_id: Optional[int] = None) -> List[Category]:
    query = db.query(Category)
    if parent_id is not None:
        query = query.filter(Category.parent_id == parent_id)
    return query.order_by(Category.sort_order).all()


def build_category_tree(db: Session) -> List[dict]:
    def build_tree(parent_id: Optional[int]) -> List[dict]:
        nodes = db.query(Category).filter(Category.parent_id == parent_id).order_by(Category.sort_order).all()
        result = []
        for node in nodes:
            result.append({
                "id": node.id,
                "parent_id": node.parent_id,
                "level": node.level,
                "code": node.code,
                "name": node.name,
                "description": node.description,
                "sort_order": node.sort_order,
                "created_at": node.created_at,
                "children": build_tree(node.id),
            })
        return result
    return build_tree(None)


def create_category(db: Session, obj_in: CategoryCreate) -> Category:
    db_obj = Category(**obj_in.model_dump())
    db.add(db_obj)
    db.commit()
    db.refresh(db_obj)
    return db_obj


def update_category(db: Session, db_obj: Category, obj_in: CategoryUpdate) -> Category:
    update_data = obj_in.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(db_obj, field, value)
    db.commit()
    db.refresh(db_obj)
    return db_obj


def delete_category(db: Session, category_id: int) -> None:
    db_obj = get_category(db, category_id)
    if not db_obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="分类不存在")
    # Check children
    children = db.query(Category).filter(Category.parent_id == category_id).first()
    if children:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="存在子分类，无法删除")
    db.delete(db_obj)
    db.commit()


def list_data_levels(db: Session) -> List[DataLevel]:
    return db.query(DataLevel).order_by(DataLevel.sort_order).all()


def get_data_level(db: Session, level_id: int) -> Optional[DataLevel]:
    return db.query(DataLevel).filter(DataLevel.id == level_id).first()


def create_data_level(db: Session, code: str, name: str, description: str, color: str, sort_order: int = 0, control_requirements: Optional[dict] = None) -> DataLevel:
    db_obj = DataLevel(code=code, name=name, description=description, color=color, sort_order=sort_order, control_requirements=control_requirements)
    db.add(db_obj)
    db.commit()
    db.refresh(db_obj)
    return db_obj


def get_rule(db: Session, rule_id: int) -> Optional[RecognitionRule]:
    return db.query(RecognitionRule).filter(RecognitionRule.id == rule_id).first()


def list_rules(db: Session, template_id: Optional[int] = None, keyword: Optional[str] = None, page: int = 1, page_size: int = 20) -> Tuple[List[RecognitionRule], int]:
    query = db.query(RecognitionRule)
    if template_id:
        query = query.filter(RecognitionRule.template_id == template_id)
    if keyword:
        query = query.filter(
            (RecognitionRule.rule_name.contains(keyword)) | (RecognitionRule.rule_content.contains(keyword))
        )
    total = query.count()
    items = query.offset((page - 1) * page_size).limit(page_size).all()
    return items, total


def create_rule(db: Session, obj_in: RecognitionRuleCreate) -> RecognitionRule:
    db_obj = RecognitionRule(**obj_in.model_dump())
    db.add(db_obj)
    db.commit()
    db.refresh(db_obj)
    return db_obj


def update_rule(db: Session, db_obj: RecognitionRule, obj_in: RecognitionRuleUpdate) -> RecognitionRule:
    update_data = obj_in.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(db_obj, field, value)
    db.commit()
    db.refresh(db_obj)
    return db_obj


def delete_rule(db: Session, rule_id: int) -> None:
    db_obj = get_rule(db, rule_id)
    if not db_obj:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="规则不存在")
    db.delete(db_obj)
    db.commit()


def get_template(db: Session, template_id: int) -> Optional[ClassificationTemplate]:
    return db.query(ClassificationTemplate).filter(ClassificationTemplate.id == template_id).first()


def list_templates(db: Session) -> List[ClassificationTemplate]:
    return db.query(ClassificationTemplate).order_by(ClassificationTemplate.id).all()


def init_builtin_data(db: Session):
    # Data Levels
    if not db.query(DataLevel).first():
        levels = [
            ("L1", "公开级", "可对外公开发布", "#67c23a", 1, {"storage": "无特殊要求", "access": "公开访问"}),
            ("L2", "内部级", "公司内部共享使用", "#409eff", 2, {"storage": "内部环境", "access": "内部员工"}),
            ("L3", "敏感级", "部门/授权人员访问，外部分享需审批", "#e6a23c", 3, {"storage": "加密存储", "access": "授权访问"}),
            ("L4", "重要级", "严格授权管理，外部分享需严格审批", "#f56c6c", 4, {"storage": "强加密", "access": "最小权限"}),
            ("L5", "核心级", "禁止对外共享", "#909399", 5, {"storage": "物理隔离", "access": "核心人员"}),
        ]
        for code, name, desc, color, sort, ctrl in levels:
            create_data_level(db, code, name, desc, color, sort, ctrl)

    # Categories
    if not db.query(Category).first():
        categories = [
            # Level 1
            {"code": "CUST", "name": "客户数据", "level": 1, "sort_order": 1},
            {"code": "POLICY", "name": "保单数据", "level": 1, "sort_order": 2},
            {"code": "CLAIM", "name": "理赔数据", "level": 1, "sort_order": 3},
            {"code": "FIN", "name": "财务数据", "level": 1, "sort_order": 4},
            {"code": "CHANNEL", "name": "渠道数据", "level": 1, "sort_order": 5},
            {"code": "REG", "name": "监管报送数据", "level": 1, "sort_order": 6},
            {"code": "INTERNAL", "name": "内部管理数据", "level": 1, "sort_order": 7},
            {"code": "SUBJECT", "name": "车辆/财产标的数据", "level": 1, "sort_order": 8},
        ]
        cat_map = {}
        for c in categories:
            obj = Category(parent_id=None, level=c["level"], code=c["code"], name=c["name"], sort_order=c["sort_order"])
            db.add(obj)
            db.commit()
            db.refresh(obj)
            cat_map[c["code"]] = obj.id

        # Level 2
        sub_categories = [
            {"parent_code": "CUST", "code": "CUST_PERSONAL", "name": "个人客户信息", "sort_order": 1},
            {"parent_code": "CUST", "code": "CUST_ENTERPRISE", "name": "企业客户信息", "sort_order": 2},
            {"parent_code": "CUST", "code": "CUST_BENEFICIARY", "name": "受益人信息", "sort_order": 3},
            {"parent_code": "POLICY", "code": "POLICY_APPLY", "name": "投保信息", "sort_order": 1},
            {"parent_code": "POLICY", "code": "POLICY_UNDERWRITE", "name": "承保信息", "sort_order": 2},
            {"parent_code": "POLICY", "code": "POLICY_RENEW", "name": "续保信息", "sort_order": 3},
            {"parent_code": "CLAIM", "code": "CLAIM_REPORT", "name": "报案信息", "sort_order": 1},
            {"parent_code": "CLAIM", "code": "CLAIM_SURVEY", "name": "查勘定损信息", "sort_order": 2},
            {"parent_code": "CLAIM", "code": "CLAIM_PAY", "name": "赔付信息", "sort_order": 3},
            {"parent_code": "FIN", "code": "FIN_PAYMENT", "name": "收付费数据", "sort_order": 1},
            {"parent_code": "FIN", "code": "FIN_RESERVE", "name": "准备金数据", "sort_order": 2},
            {"parent_code": "FIN", "code": "FIN_INVEST", "name": "投资数据", "sort_order": 3},
            {"parent_code": "CHANNEL", "code": "CHN_AGENT", "name": "代理人/经纪人信息", "sort_order": 1},
            {"parent_code": "CHANNEL", "code": "CHN_PARTNER", "name": "第三方合作方", "sort_order": 2},
            {"parent_code": "REG", "code": "REG_SOLVENCY", "name": "偿付能力数据", "sort_order": 1},
            {"parent_code": "REG", "code": "REG_STAT", "name": "统计报表数据", "sort_order": 2},
            {"parent_code": "INTERNAL", "code": "INT_EMPLOYEE", "name": "员工信息", "sort_order": 1},
            {"parent_code": "INTERNAL", "code": "INT_OPS", "name": "系统运维数据", "sort_order": 2},
            {"parent_code": "SUBJECT", "code": "SUB_VEHICLE", "name": "车辆信息", "sort_order": 1},
            {"parent_code": "SUBJECT", "code": "SUB_PROPERTY", "name": "财产标的", "sort_order": 2},
        ]
        for sc in sub_categories:
            parent_id = cat_map.get(sc["parent_code"])
            if parent_id:
                obj = Category(parent_id=parent_id, level=2, code=sc["code"], name=sc["name"], sort_order=sc["sort_order"])
                db.add(obj)
        db.commit()

    # Template
    if not db.query(ClassificationTemplate).first():
        tpl = ClassificationTemplate(
            name="财产保险行业分类分级模板",
            industry_type="insurance_property",
            version="1.0",
            description="基于《金融数据安全 数据安全分级指南》及保险行业特点制定的分类分级模板",
            is_builtin=True,
            is_active=True,
        )
        db.add(tpl)
        db.commit()
        db.refresh(tpl)

        # Create some sample rules
        level_l4 = db.query(DataLevel).filter(DataLevel.code == "L4").first()
        level_l3 = db.query(DataLevel).filter(DataLevel.code == "L3").first()
        level_l5 = db.query(DataLevel).filter(DataLevel.code == "L5").first()
        cat_cust_personal = db.query(Category).filter(Category.code == "CUST_PERSONAL").first()
        cat_fin_reserve = db.query(Category).filter(Category.code == "FIN_RESERVE").first()
        cat_int_ops = db.query(Category).filter(Category.code == "INT_OPS").first()

        rules = []
        if cat_cust_personal and level_l4:
            rules.append(RecognitionRule(
                template_id=tpl.id,
                category_id=cat_cust_personal.id,
                level_id=level_l4.id,
                rule_type="regex",
                rule_name="身份证号识别",
                rule_content=r"(\d{15}|\d{18}|\d{17}[xX])",
                target_field="sample_data",
                priority=10,
            ))
            rules.append(RecognitionRule(
                template_id=tpl.id,
                category_id=cat_cust_personal.id,
                level_id=level_l4.id,
                rule_type="keyword",
                rule_name="手机号字段识别",
                rule_content="手机,mobile,phone,telephone,tel",
                target_field="column_name",
                priority=20,
            ))
        if cat_fin_reserve and level_l5:
            rules.append(RecognitionRule(
                template_id=tpl.id,
                category_id=cat_fin_reserve.id,
                level_id=level_l5.id,
                rule_type="keyword",
                rule_name="精算模型识别",
                rule_content="精算,actuarial,准备金,reserve,偿付能力,solvency",
                target_field="column_name",
                priority=10,
            ))
        if cat_int_ops and level_l5:
            rules.append(RecognitionRule(
                template_id=tpl.id,
                category_id=cat_int_ops.id,
                level_id=level_l5.id,
                rule_type="keyword",
                rule_name="密码密钥识别",
                rule_content="password,secret,key,token,密钥,密码",
                target_field="column_name",
                priority=5,
            ))

        for r in rules:
            db.add(r)
        db.commit()
