from django.db import models
from django.contrib.postgres.fields import ArrayField

from ..common.definitions import ScanStatus




class SoftDeleteManager(models.Manager):
    """软删除管理器：默认只返回未删除的记录"""
    
    def get_queryset(self):
        return super().get_queryset().filter(deleted_at__isnull=True)


class Scan(models.Model):
    """扫描任务模型"""

    id = models.AutoField(primary_key=True)

    target = models.ForeignKey('targets.Target', on_delete=models.CASCADE, related_name='scans', help_text='扫描目标')

    # 多引擎支持字段
    engine_ids = ArrayField(
        models.IntegerField(),
        default=list,
        help_text='引擎 ID 列表'
    )
    engine_names = models.JSONField(
        default=list,
        help_text='引擎名称列表，如 ["引擎A", "引擎B"]'
    )
    merged_configuration = models.TextField(
        default='',
        help_text='合并后的 YAML 配置'
    )

    created_at = models.DateTimeField(auto_now_add=True, help_text='任务创建时间')
    stopped_at = models.DateTimeField(null=True, blank=True, help_text='扫描结束时间')

    status = models.CharField(
        max_length=20,
        choices=ScanStatus.choices,
        default=ScanStatus.INITIATED,
        db_index=True,
        help_text='任务状态'
    )

    results_dir = models.CharField(max_length=100, blank=True, default='', help_text='结果存储目录')

    container_ids = ArrayField(
        models.CharField(max_length=100),
        blank=True,
        default=list,
        help_text='容器 ID 列表（Docker Container ID）'
    )
    
    worker = models.ForeignKey(
        'engine.WorkerNode',
        on_delete=models.SET_NULL,
        related_name='scans',
        null=True,
        blank=True,
        help_text='执行扫描的 Worker 节点'
    )

    error_message = models.CharField(max_length=2000, blank=True, default='', help_text='错误信息')

    # ==================== 软删除字段 ====================
    deleted_at = models.DateTimeField(null=True, blank=True, db_index=True, help_text='删除时间（NULL表示未删除）')

    # ==================== 管理器 ====================
    objects = SoftDeleteManager()  # 默认管理器：只返回未删除的记录
    all_objects = models.Manager()  # 全量管理器：包括已删除的记录（用于硬删除）

    # ==================== 进度跟踪字段 ====================
    progress = models.IntegerField(default=0, help_text='扫描进度 0-100')
    current_stage = models.CharField(max_length=50, blank=True, default='', help_text='当前扫描阶段')
    stage_progress = models.JSONField(default=dict, help_text='各阶段进度详情')

    # ==================== 缓存统计字段 ====================
    cached_subdomains_count = models.IntegerField(default=0, help_text='缓存的子域名数量')
    cached_websites_count = models.IntegerField(default=0, help_text='缓存的网站数量')
    cached_endpoints_count = models.IntegerField(default=0, help_text='缓存的端点数量')
    cached_ips_count = models.IntegerField(default=0, help_text='缓存的IP地址数量')
    cached_directories_count = models.IntegerField(default=0, help_text='缓存的目录数量')
    cached_vulns_total = models.IntegerField(default=0, help_text='缓存的漏洞总数')
    cached_vulns_critical = models.IntegerField(default=0, help_text='缓存的严重漏洞数量')
    cached_vulns_high = models.IntegerField(default=0, help_text='缓存的高危漏洞数量')
    cached_vulns_medium = models.IntegerField(default=0, help_text='缓存的中危漏洞数量')
    cached_vulns_low = models.IntegerField(default=0, help_text='缓存的低危漏洞数量')
    stats_updated_at = models.DateTimeField(null=True, blank=True, help_text='统计数据最后更新时间')

    class Meta:
        db_table = 'scan'
        verbose_name = '扫描任务'
        verbose_name_plural = '扫描任务'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['-created_at']),  # 优化按创建时间降序排序（list 查询的默认排序）
            models.Index(fields=['target']),  # 优化按目标查询扫描任务
            models.Index(fields=['deleted_at', '-created_at']),  # 软删除 + 时间索引
        ]

    def __str__(self):
        return f"Scan #{self.id} - {self.target.name}"


class ScheduledScan(models.Model):
    """
    定时扫描任务模型
    
    调度机制：
    - APScheduler 每分钟检查 next_run_time
    - 到期任务通过 task_distributor 分发到 Worker 执行
    - 支持 cron 表达式进行灵活调度
    
    扫描模式（二选一）：
    - 组织扫描：设置 organization，执行时动态获取组织下所有目标
    - 目标扫描：设置 target，扫描单个目标
    - organization 优先级高于 target
    """
    
    id = models.AutoField(primary_key=True)
    
    # 基本信息
    name = models.CharField(max_length=200, help_text='任务名称')
    
    # 多引擎支持字段
    engine_ids = ArrayField(
        models.IntegerField(),
        default=list,
        help_text='引擎 ID 列表'
    )
    engine_names = models.JSONField(
        default=list,
        help_text='引擎名称列表，如 ["引擎A", "引擎B"]'
    )
    merged_configuration = models.TextField(
        default='',
        help_text='合并后的 YAML 配置'
    )
    
    # 关联的组织（组织扫描模式：执行时动态获取组织下所有目标）
    organization = models.ForeignKey(
        'targets.Organization',
        on_delete=models.CASCADE,
        related_name='scheduled_scans',
        null=True,
        blank=True,
        help_text='扫描组织（设置后执行时动态获取组织下所有目标）'
    )
    
    # 关联的目标（目标扫描模式：扫描单个目标）
    target = models.ForeignKey(
        'targets.Target',
        on_delete=models.CASCADE,
        related_name='scheduled_scans',
        null=True,
        blank=True,
        help_text='扫描单个目标（与 organization 二选一）'
    )
    
    # 调度配置 - 直接使用 Cron 表达式
    cron_expression = models.CharField(
        max_length=100,
        default='0 2 * * *',
        help_text='Cron 表达式，格式：分 时 日 月 周'
    )
    
    # 状态
    is_enabled = models.BooleanField(default=True, db_index=True, help_text='是否启用')
    
    # 执行统计
    run_count = models.IntegerField(default=0, help_text='已执行次数')
    last_run_time = models.DateTimeField(null=True, blank=True, help_text='上次执行时间')
    next_run_time = models.DateTimeField(null=True, blank=True, help_text='下次执行时间')
    
    # 时间戳
    created_at = models.DateTimeField(auto_now_add=True, help_text='创建时间')
    updated_at = models.DateTimeField(auto_now=True, help_text='更新时间')
    
    class Meta:
        db_table = 'scheduled_scan'
        verbose_name = '定时扫描任务'
        verbose_name_plural = '定时扫描任务'
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['-created_at']),
            models.Index(fields=['is_enabled', '-created_at']),
            models.Index(fields=['name']),  # 优化 name 搜索
        ]
    
    def __str__(self):
        return f"ScheduledScan #{self.id} - {self.name}"