"""
InsightFlow Phase 7 Task 3: 数据安全与合规模块
Security Manager - 端到端加密、数据脱敏、审计日志
"""

import base64
import hashlib
import json
import re
import secrets
import sqlite3
from dataclasses import asdict, dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from typing import Any

# 加密相关
try:
    from cryptography.fernet import Fernet
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

    CRYPTO_AVAILABLE = True
except ImportError:
    CRYPTO_AVAILABLE = False
    print("Warning: cryptography not available, encryption features disabled")


class AuditActionType(Enum):
    """审计动作类型"""

    CREATE = "create"
    READ = "read"
    UPDATE = "update"
    DELETE = "delete"
    LOGIN = "login"
    LOGOUT = "logout"
    EXPORT = "export"
    IMPORT = "import"
    SHARE = "share"
    PERMISSION_CHANGE = "permission_change"
    ENCRYPTION_ENABLE = "encryption_enable"
    ENCRYPTION_DISABLE = "encryption_disable"
    DATA_MASKING = "data_masking"
    API_KEY_CREATE = "api_key_create"
    API_KEY_REVOKE = "api_key_revoke"
    WORKFLOW_TRIGGER = "workflow_trigger"
    WEBHOOK_SEND = "webhook_send"
    BOT_MESSAGE = "bot_message"


class DataSensitivityLevel(Enum):
    """数据敏感度级别"""

    PUBLIC = "public"  # 公开
    INTERNAL = "internal"  # 内部
    CONFIDENTIAL = "confidential"  # 机密
    SECRET = "secret"  # 绝密


class MaskingRuleType(Enum):
    """脱敏规则类型"""

    PHONE = "phone"  # 手机号
    EMAIL = "email"  # 邮箱
    ID_CARD = "id_card"  # 身份证号
    BANK_CARD = "bank_card"  # 银行卡号
    NAME = "name"  # 姓名
    ADDRESS = "address"  # 地址
    CUSTOM = "custom"  # 自定义


@dataclass
class AuditLog:
    """审计日志条目"""

    id: str
    action_type: str
    user_id: str | None = None
    user_ip: str | None = None
    user_agent: str | None = None
    resource_type: str | None = None  # project, entity, transcript, etc.
    resource_id: str | None = None
    action_details: str | None = None  # JSON string
    before_value: str | None = None
    after_value: str | None = None
    success: bool = True
    error_message: str | None = None
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)


@dataclass
class EncryptionConfig:
    """加密配置"""

    id: str
    project_id: str
    is_enabled: bool = False
    encryption_type: str = "aes-256-gcm"  # aes-256-gcm, chacha20-poly1305
    key_derivation: str = "pbkdf2"  # pbkdf2, argon2
    master_key_hash: str | None = None  # 主密钥哈希（用于验证）
    salt: str | None = None
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    updated_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)


@dataclass
class MaskingRule:
    """脱敏规则"""

    id: str
    project_id: str
    name: str
    rule_type: str  # phone, email, id_card, bank_card, name, address, custom
    pattern: str  # 正则表达式
    replacement: str  # 替换模板，如 "****"
    is_active: bool = True
    priority: int = 0
    description: str | None = None
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    updated_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)


@dataclass
class DataAccessPolicy:
    """数据访问策略"""

    id: str
    project_id: str
    name: str
    description: str | None = None
    allowed_users: str | None = None  # JSON array of user IDs
    allowed_roles: str | None = None  # JSON array of roles
    allowed_ips: str | None = None  # JSON array of IP patterns
    time_restrictions: str | None = None  # JSON: {"start_time": "09:00", "end_time": "18:00"}
    max_access_count: int | None = None  # 最大访问次数
    require_approval: bool = False
    is_active: bool = True
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    updated_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)


@dataclass
class AccessRequest:
    """访问请求（用于需要审批的访问）"""

    id: str
    policy_id: str
    user_id: str
    request_reason: str | None = None
    status: str = "pending"  # pending, approved, rejected, expired
    approved_by: str | None = None
    approved_at: str | None = None
    expires_at: str | None = None
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())

    def to_dict(self) -> dict[str, Any]:
        return asdict(self)


class SecurityManager:
    """安全管理器"""

    # 预定义脱敏规则
    DEFAULT_MASKING_RULES = {
        MaskingRuleType.PHONE: {"pattern": r"(\d{3})\d{4}(\d{4})", "replacement": r"\1****\2"},
        MaskingRuleType.EMAIL: {"pattern": r"(\w{1,3})\w+(@\w+\.\w+)", "replacement": r"\1***\2"},
        MaskingRuleType.ID_CARD: {
            "pattern": r"(\d{6})\d{8}(\d{4})",
            "replacement": r"\1********\2",
        },
        MaskingRuleType.BANK_CARD: {
            "pattern": r"(\d{4})\d+(\d{4})",
            "replacement": r"\1 **** **** \2",
        },
        MaskingRuleType.NAME: {
            "pattern": r"([\u4e00-\u9fa5])[\u4e00-\u9fa5]+",
            "replacement": r"\1**",
        },
        MaskingRuleType.ADDRESS: {
            "pattern": r"([\u4e00-\u9fa5]{2,})([\u4e00-\u9fa5]+路|街|巷|号)(.+)",
            "replacement": r"\1\2***",
        },
    }

    def __init__(self, db_path: str = "insightflow.db"):
        self.db_path = db_path
        # 预编译正则缓存
        self._compiled_patterns: dict[str, re.Pattern] = {}
        self._local = {}
        self._init_db()

    def _init_db(self) -> None:
        """初始化数据库表"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        # 审计日志表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS audit_logs (
                id TEXT PRIMARY KEY,
                action_type TEXT NOT NULL,
                user_id TEXT,
                user_ip TEXT,
                user_agent TEXT,
                resource_type TEXT,
                resource_id TEXT,
                action_details TEXT,
                before_value TEXT,
                after_value TEXT,
                success INTEGER DEFAULT 1,
                error_message TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        """)

        # 加密配置表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS encryption_configs (
                id TEXT PRIMARY KEY,
                project_id TEXT NOT NULL,
                is_enabled INTEGER DEFAULT 0,
                encryption_type TEXT DEFAULT 'aes-256-gcm',
                key_derivation TEXT DEFAULT 'pbkdf2',
                master_key_hash TEXT,
                salt TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (project_id) REFERENCES projects(id)
            )
        """)

        # 脱敏规则表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS masking_rules (
                id TEXT PRIMARY KEY,
                project_id TEXT NOT NULL,
                name TEXT NOT NULL,
                rule_type TEXT NOT NULL,
                pattern TEXT NOT NULL,
                replacement TEXT NOT NULL,
                is_active INTEGER DEFAULT 1,
                priority INTEGER DEFAULT 0,
                description TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (project_id) REFERENCES projects(id)
            )
        """)

        # 数据访问策略表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS data_access_policies (
                id TEXT PRIMARY KEY,
                project_id TEXT NOT NULL,
                name TEXT NOT NULL,
                description TEXT,
                allowed_users TEXT,
                allowed_roles TEXT,
                allowed_ips TEXT,
                time_restrictions TEXT,
                max_access_count INTEGER,
                require_approval INTEGER DEFAULT 0,
                is_active INTEGER DEFAULT 1,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (project_id) REFERENCES projects(id)
            )
        """)

        # 访问请求表
        cursor.execute("""
            CREATE TABLE IF NOT EXISTS access_requests (
                id TEXT PRIMARY KEY,
                policy_id TEXT NOT NULL,
                user_id TEXT NOT NULL,
                request_reason TEXT,
                status TEXT DEFAULT 'pending',
                approved_by TEXT,
                approved_at TIMESTAMP,
                expires_at TIMESTAMP,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (policy_id) REFERENCES data_access_policies(id)
            )
        """)

        # 创建索引
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_audit_logs_resource "
            "ON audit_logs(resource_type, resource_id)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit_logs(action_type)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit_logs(created_at)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_encryption_project ON encryption_configs(project_id)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_masking_project ON masking_rules(project_id)"
        )
        cursor.execute(
            "CREATE INDEX IF NOT EXISTS idx_access_policy_project ON data_access_policies(project_id)"
        )

        conn.commit()
        conn.close()

    def _generate_id(self) -> str:
        """生成唯一ID"""
        return hashlib.sha256(
            f"{datetime.now().isoformat()}{secrets.token_hex(16)}".encode()
        ).hexdigest()[:32]

    # ==================== 审计日志 ====================

    def log_audit(
        self,
        action_type: AuditActionType,
        user_id: str | None = None,
        user_ip: str | None = None,
        user_agent: str | None = None,
        resource_type: str | None = None,
        resource_id: str | None = None,
        action_details: dict | None = None,
        before_value: str | None = None,
        after_value: str | None = None,
        success: bool = True,
        error_message: str | None = None,
    ) -> AuditLog:
        """记录审计日志"""
        log = AuditLog(
            id=self._generate_id(),
            action_type=action_type.value,
            user_id=user_id,
            user_ip=user_ip,
            user_agent=user_agent,
            resource_type=resource_type,
            resource_id=resource_id,
            action_details=json.dumps(action_details) if action_details else None,
            before_value=before_value,
            after_value=after_value,
            success=success,
            error_message=error_message,
        )

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute(
            """
            INSERT INTO audit_logs
            (id, action_type, user_id, user_ip, user_agent, resource_type, resource_id,
             action_details, before_value, after_value, success, error_message, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
            (
                log.id,
                log.action_type,
                log.user_id,
                log.user_ip,
                log.user_agent,
                log.resource_type,
                log.resource_id,
                log.action_details,
                log.before_value,
                log.after_value,
                int(log.success),
                log.error_message,
                log.created_at,
            ),
        )
        conn.commit()
        conn.close()

        return log

    def get_audit_logs(
        self,
        user_id: str | None = None,
        resource_type: str | None = None,
        resource_id: str | None = None,
        action_type: str | None = None,
        start_time: str | None = None,
        end_time: str | None = None,
        success: bool | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> list[AuditLog]:
        """查询审计日志"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        query = "SELECT * FROM audit_logs WHERE 1=1"
        params = []

        if user_id:
            query += " AND user_id = ?"
            params.append(user_id)
        if resource_type:
            query += " AND resource_type = ?"
            params.append(resource_type)
        if resource_id:
            query += " AND resource_id = ?"
            params.append(resource_id)
        if action_type:
            query += " AND action_type = ?"
            params.append(action_type)
        if start_time:
            query += " AND created_at >= ?"
            params.append(start_time)
        if end_time:
            query += " AND created_at <= ?"
            params.append(end_time)
        if success is not None:
            query += " AND success = ?"
            params.append(int(success))

        query += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
        params.extend([limit, offset])

        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()

        logs = []
        col_names = [desc[0] for desc in cursor.description] if cursor.description else []
        if not col_names:
            return logs

        for row in rows:
            log = AuditLog(
                id=row[0],
                action_type=row[1],
                user_id=row[2],
                user_ip=row[3],
                user_agent=row[4],
                resource_type=row[5],
                resource_id=row[6],
                action_details=row[7],
                before_value=row[8],
                after_value=row[9],
                success=bool(row[10]),
                error_message=row[11],
                created_at=row[12],
            )
            logs.append(log)

        conn.close()
        return logs

    def get_audit_stats(
        self, start_time: str | None = None, end_time: str | None = None
    ) -> dict[str, Any]:
        """获取审计统计"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        query = "SELECT action_type, success, COUNT(*) FROM audit_logs WHERE 1=1"
        params = []

        if start_time:
            query += " AND created_at >= ?"
            params.append(start_time)
        if end_time:
            query += " AND created_at <= ?"
            params.append(end_time)

        query += " GROUP BY action_type, success"

        cursor.execute(query, params)
        rows = cursor.fetchall()

        stats = {"total_actions": 0, "success_count": 0, "failure_count": 0, "action_breakdown": {}}

        for action_type, success, count in rows:
            stats["total_actions"] += count
            if success:
                stats["success_count"] += count
            else:
                stats["failure_count"] += count

            if action_type not in stats["action_breakdown"]:
                stats["action_breakdown"][action_type] = {"success": 0, "failure": 0}

            if success:
                stats["action_breakdown"][action_type]["success"] += count
            else:
                stats["action_breakdown"][action_type]["failure"] += count

        conn.close()
        return stats

    # ==================== 端到端加密 ====================

    def _derive_key(self, password: str, salt: bytes) -> bytes:
        """从密码派生密钥"""
        if not CRYPTO_AVAILABLE:
            raise RuntimeError("cryptography library not available")

        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000,
        )
        return base64.urlsafe_b64encode(kdf.derive(password.encode()))

    def enable_encryption(self, project_id: str, master_password: str) -> EncryptionConfig:
        """启用项目加密"""
        if not CRYPTO_AVAILABLE:
            raise RuntimeError("cryptography library not available")

        # 生成盐值
        salt = secrets.token_hex(16)

        # 派生密钥并哈希（用于验证）
        key = self._derive_key(master_password, salt.encode())
        key_hash = hashlib.sha256(key).hexdigest()

        config = EncryptionConfig(
            id=self._generate_id(),
            project_id=project_id,
            is_enabled=True,
            encryption_type="aes-256-gcm",
            key_derivation="pbkdf2",
            master_key_hash=key_hash,
            salt=salt,
        )

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        # 检查是否已存在配置
        cursor.execute("SELECT id FROM encryption_configs WHERE project_id = ?", (project_id,))
        existing = cursor.fetchone()

        if existing:
            cursor.execute(
                """
                UPDATE encryption_configs
                SET is_enabled = 1, encryption_type = ?, key_derivation = ?,
                    master_key_hash = ?, salt = ?, updated_at = ?
                WHERE project_id = ?
            """,
                (
                    config.encryption_type,
                    config.key_derivation,
                    config.master_key_hash,
                    config.salt,
                    config.updated_at,
                    project_id,
                ),
            )
            config.id = existing[0]
        else:
            cursor.execute(
                """
                INSERT INTO encryption_configs
                (id, project_id, is_enabled, encryption_type, key_derivation,
                 master_key_hash, salt, created_at, updated_at)
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
                (
                    config.id,
                    config.project_id,
                    int(config.is_enabled),
                    config.encryption_type,
                    config.key_derivation,
                    config.master_key_hash,
                    config.salt,
                    config.created_at,
                    config.updated_at,
                ),
            )

        conn.commit()
        conn.close()

        # 记录审计日志
        self.log_audit(
            action_type=AuditActionType.ENCRYPTION_ENABLE,
            resource_type="project",
            resource_id=project_id,
            action_details={"encryption_type": config.encryption_type},
        )

        return config

    def disable_encryption(self, project_id: str, master_password: str) -> bool:
        """禁用项目加密"""
        # 验证密码
        if not self.verify_encryption_password(project_id, master_password):
            return False

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            """
            UPDATE encryption_configs
            SET is_enabled = 0, updated_at = ?
            WHERE project_id = ?
        """,
            (datetime.now().isoformat(), project_id),
        )

        conn.commit()
        conn.close()

        # 记录审计日志
        self.log_audit(
            action_type=AuditActionType.ENCRYPTION_DISABLE,
            resource_type="project",
            resource_id=project_id,
        )

        return True

    def verify_encryption_password(self, project_id: str, password: str) -> bool:
        """验证加密密码"""
        if not CRYPTO_AVAILABLE:
            return False

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            "SELECT master_key_hash, salt FROM encryption_configs WHERE project_id = ?",
            (project_id,),
        )
        row = cursor.fetchone()
        conn.close()

        if not row:
            return False

        stored_hash, salt = row
        key = self._derive_key(password, salt.encode())
        key_hash = hashlib.sha256(key).hexdigest()

        return key_hash == stored_hash

    def get_encryption_config(self, project_id: str) -> EncryptionConfig | None:
        """获取加密配置"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute("SELECT * FROM encryption_configs WHERE project_id = ?", (project_id,))
        row = cursor.fetchone()
        conn.close()

        if not row:
            return None

        return EncryptionConfig(
            id=row[0],
            project_id=row[1],
            is_enabled=bool(row[2]),
            encryption_type=row[3],
            key_derivation=row[4],
            master_key_hash=row[5],
            salt=row[6],
            created_at=row[7],
            updated_at=row[8],
        )

    def encrypt_data(self, data: str, password: str, salt: str | None = None) -> tuple[str, str]:
        """加密数据"""
        if not CRYPTO_AVAILABLE:
            raise RuntimeError("cryptography library not available")

        if salt is None:
            salt = secrets.token_hex(16)

        key = self._derive_key(password, salt.encode())
        f = Fernet(key)
        encrypted = f.encrypt(data.encode())

        return base64.b64encode(encrypted).decode(), salt

    def decrypt_data(self, encrypted_data: str, password: str, salt: str) -> str:
        """解密数据"""
        if not CRYPTO_AVAILABLE:
            raise RuntimeError("cryptography library not available")

        key = self._derive_key(password, salt.encode())
        f = Fernet(key)
        decrypted = f.decrypt(base64.b64decode(encrypted_data))

        return decrypted.decode()

    # ==================== 数据脱敏 ====================

    def create_masking_rule(
        self,
        project_id: str,
        name: str,
        rule_type: MaskingRuleType,
        pattern: str | None = None,
        replacement: str | None = None,
        description: str | None = None,
        priority: int = 0,
    ) -> MaskingRule:
        """创建脱敏规则"""
        # 使用预定义规则或自定义规则
        if rule_type in self.DEFAULT_MASKING_RULES and not pattern:
            default = self.DEFAULT_MASKING_RULES[rule_type]
            pattern = default["pattern"]
            replacement = replacement or default["replacement"]

        rule = MaskingRule(
            id=self._generate_id(),
            project_id=project_id,
            name=name,
            rule_type=rule_type.value,
            pattern=pattern or "",
            replacement=replacement or "****",
            description=description,
            priority=priority,
        )

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO masking_rules
            (id, project_id, name, rule_type, pattern, replacement,
             is_active, priority, description, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
            (
                rule.id,
                rule.project_id,
                rule.name,
                rule.rule_type,
                rule.pattern,
                rule.replacement,
                int(rule.is_active),
                rule.priority,
                rule.description,
                rule.created_at,
                rule.updated_at,
            ),
        )

        conn.commit()
        conn.close()

        # 记录审计日志
        self.log_audit(
            action_type=AuditActionType.DATA_MASKING,
            resource_type="project",
            resource_id=project_id,
            action_details={"action": "create_rule", "rule_name": name},
        )

        return rule

    def get_masking_rules(self, project_id: str, active_only: bool = True) -> list[MaskingRule]:
        """获取脱敏规则"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        query = "SELECT * FROM masking_rules WHERE project_id = ?"
        params = [project_id]

        if active_only:
            query += " AND is_active = 1"

        query += " ORDER BY priority DESC"

        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()

        rules = []
        for row in rows:
            rules.append(
                MaskingRule(
                    id=row[0],
                    project_id=row[1],
                    name=row[2],
                    rule_type=row[3],
                    pattern=row[4],
                    replacement=row[5],
                    is_active=bool(row[6]),
                    priority=row[7],
                    description=row[8],
                    created_at=row[9],
                    updated_at=row[10],
                )
            )

        return rules

    def update_masking_rule(self, rule_id: str, **kwargs) -> MaskingRule | None:
        """更新脱敏规则"""
        allowed_fields = ["name", "pattern", "replacement", "is_active", "priority", "description"]

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        set_clauses = []
        params = []

        for key, value in kwargs.items():
            if key in allowed_fields:
                set_clauses.append(f"{key} = ?")
                params.append(int(value) if key == "is_active" else value)

        if not set_clauses:
            conn.close()
            return None

        set_clauses.append("updated_at = ?")
        params.append(datetime.now().isoformat())
        params.append(rule_id)

        cursor.execute(
            f"""
            UPDATE masking_rules
            SET {", ".join(set_clauses)}
            WHERE id = ?
        """,
            params,
        )

        conn.commit()
        conn.close()

        # 获取更新后的规则
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM masking_rules WHERE id = ?", (rule_id,))
        row = cursor.fetchone()
        conn.close()

        if not row:
            return None

        return MaskingRule(
            id=row[0],
            project_id=row[1],
            name=row[2],
            rule_type=row[3],
            pattern=row[4],
            replacement=row[5],
            is_active=bool(row[6]),
            priority=row[7],
            description=row[8],
            created_at=row[9],
            updated_at=row[10],
        )

    def delete_masking_rule(self, rule_id: str) -> bool:
        """删除脱敏规则"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute("DELETE FROM masking_rules WHERE id = ?", (rule_id,))

        success = cursor.rowcount > 0
        conn.commit()
        conn.close()

        return success

    def apply_masking(
        self, text: str, project_id: str, rule_types: list[MaskingRuleType] | None = None
    ) -> str:
        """应用脱敏规则到文本"""
        rules = self.get_masking_rules(project_id)

        if not rules:
            return text

        masked_text = text

        for rule in rules:
            # 如果指定了规则类型，只应用指定类型的规则
            if rule_types and MaskingRuleType(rule.rule_type) not in rule_types:
                continue

            try:
                masked_text = re.sub(rule.pattern, rule.replacement, masked_text)
            except re.error:
                # 忽略无效的正则表达式
                continue

        return masked_text

    def apply_masking_to_entity(
        self, entity_data: dict[str, Any], project_id: str
    ) -> dict[str, Any]:
        """对实体数据应用脱敏"""
        masked_data = entity_data.copy()

        # 对可能包含敏感信息的字段进行脱敏
        sensitive_fields = ["name", "definition", "description", "value"]

        for f in sensitive_fields:
            if f in masked_data and isinstance(masked_data[f], str):
                masked_data[f] = self.apply_masking(masked_data[f], project_id)

        return masked_data

    # ==================== 数据访问策略 ====================

    def create_access_policy(
        self,
        project_id: str,
        name: str,
        description: str | None = None,
        allowed_users: list[str] | None = None,
        allowed_roles: list[str] | None = None,
        allowed_ips: list[str] | None = None,
        time_restrictions: dict | None = None,
        max_access_count: int | None = None,
        require_approval: bool = False,
    ) -> DataAccessPolicy:
        """创建数据访问策略"""
        policy = DataAccessPolicy(
            id=self._generate_id(),
            project_id=project_id,
            name=name,
            description=description,
            allowed_users=json.dumps(allowed_users) if allowed_users else None,
            allowed_roles=json.dumps(allowed_roles) if allowed_roles else None,
            allowed_ips=json.dumps(allowed_ips) if allowed_ips else None,
            time_restrictions=json.dumps(time_restrictions) if time_restrictions else None,
            max_access_count=max_access_count,
            require_approval=require_approval,
        )

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO data_access_policies
            (id, project_id, name, description, allowed_users, allowed_roles,
             allowed_ips, time_restrictions, max_access_count, require_approval,
             is_active, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
            (
                policy.id,
                policy.project_id,
                policy.name,
                policy.description,
                policy.allowed_users,
                policy.allowed_roles,
                policy.allowed_ips,
                policy.time_restrictions,
                policy.max_access_count,
                int(policy.require_approval),
                int(policy.is_active),
                policy.created_at,
                policy.updated_at,
            ),
        )

        conn.commit()
        conn.close()

        return policy

    def get_access_policies(
        self, project_id: str, active_only: bool = True
    ) -> list[DataAccessPolicy]:
        """获取数据访问策略"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        query = "SELECT * FROM data_access_policies WHERE project_id = ?"
        params = [project_id]

        if active_only:
            query += " AND is_active = 1"

        cursor.execute(query, params)
        rows = cursor.fetchall()
        conn.close()

        policies = []
        for row in rows:
            policies.append(
                DataAccessPolicy(
                    id=row[0],
                    project_id=row[1],
                    name=row[2],
                    description=row[3],
                    allowed_users=row[4],
                    allowed_roles=row[5],
                    allowed_ips=row[6],
                    time_restrictions=row[7],
                    max_access_count=row[8],
                    require_approval=bool(row[9]),
                    is_active=bool(row[10]),
                    created_at=row[11],
                    updated_at=row[12],
                )
            )

        return policies

    def check_access_permission(
        self, policy_id: str, user_id: str, user_ip: str | None = None
    ) -> tuple[bool, str | None]:
        """检查访问权限"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            "SELECT * FROM data_access_policies WHERE id = ? AND is_active = 1", (policy_id,)
        )
        row = cursor.fetchone()
        conn.close()

        if not row:
            return False, "Policy not found or inactive"

        policy = DataAccessPolicy(
            id=row[0],
            project_id=row[1],
            name=row[2],
            description=row[3],
            allowed_users=row[4],
            allowed_roles=row[5],
            allowed_ips=row[6],
            time_restrictions=row[7],
            max_access_count=row[8],
            require_approval=bool(row[9]),
            is_active=bool(row[10]),
            created_at=row[11],
            updated_at=row[12],
        )

        # 检查用户白名单
        if policy.allowed_users:
            allowed = json.loads(policy.allowed_users)
            if user_id not in allowed:
                return False, "User not in allowed list"

        # 检查IP白名单
        if policy.allowed_ips and user_ip:
            allowed_ips = json.loads(policy.allowed_ips)
            ip_allowed = False
            for ip_pattern in allowed_ips:
                if self._match_ip_pattern(user_ip, ip_pattern):
                    ip_allowed = True
                    break
            if not ip_allowed:
                return False, "IP not in allowed list"

        # 检查时间限制
        if policy.time_restrictions:
            restrictions = json.loads(policy.time_restrictions)
            now = datetime.now()

            if "start_time" in restrictions and "end_time" in restrictions:
                current_time = now.strftime("%H:%M")
                if not (restrictions["start_time"] <= current_time <= restrictions["end_time"]):
                    return False, "Access not allowed at this time"

            if "days_of_week" in restrictions:
                if now.weekday() not in restrictions["days_of_week"]:
                    return False, "Access not allowed on this day"

        # 检查是否需要审批
        if policy.require_approval:
            # 检查是否有有效的访问请求
            conn = sqlite3.connect(self.db_path)
            cursor = conn.cursor()

            cursor.execute(
                """
                SELECT * FROM access_requests
                WHERE policy_id = ? AND user_id = ? AND status = 'approved'
                AND (expires_at IS NULL OR expires_at > ?)
            """,
                (policy_id, user_id, datetime.now().isoformat()),
            )

            request = cursor.fetchone()
            conn.close()

            if not request:
                return False, "Access requires approval"

        return True, None

    def _match_ip_pattern(self, ip: str, pattern: str) -> bool:
        """匹配IP模式（支持CIDR）"""
        import ipaddress

        try:
            if "/" in pattern:
                # CIDR 表示法
                network = ipaddress.ip_network(pattern, strict=False)
                return ipaddress.ip_address(ip) in network
            else:
                # 精确匹配
                return ip == pattern
        except ValueError:
            return ip == pattern

    def create_access_request(
        self,
        policy_id: str,
        user_id: str,
        request_reason: str | None = None,
        expires_hours: int = 24,
    ) -> AccessRequest:
        """创建访问请求"""
        request = AccessRequest(
            id=self._generate_id(),
            policy_id=policy_id,
            user_id=user_id,
            request_reason=request_reason,
            expires_at=(datetime.now() + timedelta(hours=expires_hours)).isoformat(),
        )

        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            """
            INSERT INTO access_requests
            (id, policy_id, user_id, request_reason, status, expires_at, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?)
        """,
            (
                request.id,
                request.policy_id,
                request.user_id,
                request.request_reason,
                request.status,
                request.expires_at,
                request.created_at,
            ),
        )

        conn.commit()
        conn.close()

        return request

    def approve_access_request(
        self, request_id: str, approved_by: str, expires_hours: int = 24
    ) -> AccessRequest | None:
        """批准访问请求"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        expires_at = (datetime.now() + timedelta(hours=expires_hours)).isoformat()
        approved_at = datetime.now().isoformat()

        cursor.execute(
            """
            UPDATE access_requests
            SET status = 'approved', approved_by = ?, approved_at = ?, expires_at = ?
            WHERE id = ?
        """,
            (approved_by, approved_at, expires_at, request_id),
        )

        conn.commit()

        # 获取更新后的请求
        cursor.execute("SELECT * FROM access_requests WHERE id = ?", (request_id,))
        row = cursor.fetchone()
        conn.close()

        if not row:
            return None

        return AccessRequest(
            id=row[0],
            policy_id=row[1],
            user_id=row[2],
            request_reason=row[3],
            status=row[4],
            approved_by=row[5],
            approved_at=row[6],
            expires_at=row[7],
            created_at=row[8],
        )

    def reject_access_request(self, request_id: str, rejected_by: str) -> AccessRequest | None:
        """拒绝访问请求"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()

        cursor.execute(
            """
            UPDATE access_requests
            SET status = 'rejected', approved_by = ?
            WHERE id = ?
        """,
            (rejected_by, request_id),
        )

        conn.commit()

        cursor.execute("SELECT * FROM access_requests WHERE id = ?", (request_id,))
        row = cursor.fetchone()
        conn.close()

        if not row:
            return None

        return AccessRequest(
            id=row[0],
            policy_id=row[1],
            user_id=row[2],
            request_reason=row[3],
            status=row[4],
            approved_by=row[5],
            approved_at=row[6],
            expires_at=row[7],
            created_at=row[8],
        )


# 全局安全管理器实例
_security_manager = None


def get_security_manager(db_path: str = "insightflow.db") -> SecurityManager:
    """获取安全管理器实例"""
    global _security_manager
    if _security_manager is None:
        _security_manager = SecurityManager(db_path)
    return _security_manager
