import threading
import traceback
from datetime import UTC, datetime, timedelta

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.core.logging import get_logger
from app.db.session import get_session_factory
from app.models.hermes_config import HermesTaskConfig, HermesTaskExecutionLog
from app.services.digital_employee_finance_report_task import (
    DigitalEmployeeFinanceReportTaskService,
)
from app.services.hermes_employee_profile_scanner import HermesEmployeeProfileScannerService
from app.services.hermes_risk_clue_collector import HermesRiskClueCollectorService
from app.services.hermes_risk_scanner import HermesRiskScannerService

logger = get_logger("app.services.hermes_scheduler")


class HermesScheduler:
    def __init__(self) -> None:
        self._stop_event = threading.Event()
        self._thread: threading.Thread | None = None
        self._lock = threading.Lock()
        self.session_factory = get_session_factory()

    def start(self) -> None:
        with self._lock:
            if self._thread is not None and self._thread.is_alive():
                return
            self._stop_event.clear()
            self._thread = threading.Thread(
                target=self._run_loop,
                name="hermes-agent-scheduler",
                daemon=True,
            )
            self._thread.start()
            logger.info("Hermes Agent Scheduler started.")

    def shutdown(self) -> None:
        with self._lock:
            thread = self._thread
            self._thread = None
        self._stop_event.set()
        if thread is not None and thread.is_alive():
            thread.join(timeout=3)
        logger.info("Hermes Agent Scheduler stopped.")

    def _run_loop(self) -> None:
        logger.info("Hermes background loop is now active. Polling interval: 60s.")
        while not self._stop_event.is_set():
            try:
                self._check_and_run_tasks()
            except Exception as e:
                logger.error(f"Error in Hermes run loop: {e}", exc_info=True)

            # 睡眠一分钟，每分钟轮询一次
            if self._stop_event.wait(60.0):
                break

    def _check_and_run_tasks(self) -> None:
        db = self.session_factory()
        try:
            # 获取所有启用的任务配置
            stmt = select(HermesTaskConfig).where(HermesTaskConfig.is_enabled)
            configs = db.scalars(stmt).all()

            for config in configs:
                if self._should_run_now(db, config):
                    self._execute_task(db, config)

        finally:
            db.close()

    def _should_run_now(self, db: Session, config: HermesTaskConfig) -> bool:
        scheduled_at = self._resolve_last_scheduled_at(config.cron_expression)
        stmt = (
            select(HermesTaskExecutionLog)
            .where(
                HermesTaskExecutionLog.config_id == config.id,
                HermesTaskExecutionLog.tenant_id == config.tenant_id,
                HermesTaskExecutionLog.status.in_(["success", "running"]),
            )
            .order_by(HermesTaskExecutionLog.started_at.desc())
            .limit(1)
        )

        last_log = db.scalars(stmt).first()

        if not last_log:
            return True

        return last_log.started_at < scheduled_at

    def _resolve_last_scheduled_at(self, cron_expression: str | None) -> datetime:
        now = datetime.now(UTC)
        parsed = self._parse_simple_cron(cron_expression)
        if parsed is None:
            return now - timedelta(hours=23.5)

        minute, hour, weekday = parsed
        scheduled_at = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
        if weekday is None:
            if scheduled_at > now:
                scheduled_at -= timedelta(days=1)
            return scheduled_at

        days_back = (now.weekday() - weekday) % 7
        scheduled_at = (now - timedelta(days=days_back)).replace(
            hour=hour,
            minute=minute,
            second=0,
            microsecond=0,
        )
        if scheduled_at > now:
            scheduled_at -= timedelta(days=7)
        return scheduled_at

    def _parse_simple_cron(self, cron_expression: str | None) -> tuple[int, int, int | None] | None:
        parts = str(cron_expression or "").strip().split()
        if len(parts) < 5:
            return None
        minute = self._parse_cron_number(parts[0], minimum=0, maximum=59)
        hour = self._parse_cron_number(parts[1], minimum=0, maximum=23)
        if minute is None or hour is None:
            return None

        weekday: int | None = None
        if parts[4] != "*":
            raw_weekday = self._parse_cron_number(parts[4], minimum=0, maximum=7)
            if raw_weekday is None:
                return None
            weekday = 6 if raw_weekday in {0, 7} else raw_weekday - 1
        return minute, hour, weekday

    @staticmethod
    def _parse_cron_number(value: str, *, minimum: int, maximum: int) -> int | None:
        try:
            parsed = int(str(value).strip())
        except ValueError:
            return None
        if parsed < minimum or parsed > maximum:
            return None
        return parsed

    def _execute_task(self, db: Session, config: HermesTaskConfig) -> None:
        logger.info(f"Triggering Hermes task: {config.task_type} (Config ID: {config.id})")

        # 创建执行日志，标记为 running
        log_record = HermesTaskExecutionLog(
            tenant_id=config.tenant_id,
            config_id=config.id,
            status="running",
        )
        db.add(log_record)
        db.commit()
        db.refresh(log_record)

        try:
            if config.task_type == "global_risk_scan":
                scanner = HermesRiskScannerService(db)
                summary = scanner.scan_global_risks(
                    log_id=log_record.id,
                    tenant_id=config.tenant_id,
                )
                log_record.result_summary = (
                    f"风险图谱巡检完成：扫描 {summary.get('scanned_claim_count', 0)} 张单据，"
                    f"生成 {summary.get('risk_observation_count', 0)} 条风险观察。"
                )
            elif config.task_type == "weekly_expense_report":
                result = DigitalEmployeeFinanceReportTaskService(db).generate_report(
                    report_type="weekly",
                    tenant_id=config.tenant_id,
                )
                log_record.result_summary = (
                    f"财务经营周报完成：邮件状态 "
                    f"{(result.get('delivery') or {}).get('status', 'skipped')}。"
                )
            elif config.task_type == "employee_behavior_profile_scan":
                scanner = HermesEmployeeProfileScannerService(db)
                summary = scanner.scan_employee_profiles(
                    log_id=log_record.id,
                    tenant_id=config.tenant_id,
                )
                log_record.result_summary = (
                    f"员工画像巡检完成：目标 {summary.get('target_employee_count', 0)} 人，"
                    f"生成 {summary.get('snapshot_count', 0)} 条快照，"
                    f"重点关注 {summary.get('high_attention_employee_count', 0)} 人。"
                )
            elif config.task_type == "risk_clue_collect":
                collector = HermesRiskClueCollectorService(db)
                summary = collector.collect_risk_clues(
                    run_id=log_record.id,
                    tenant_id=config.tenant_id,
                )
                log_record.result_summary = (
                    f"风险线索归集完成：读取 {summary.get('fact_count', 0)} 条事实，"
                    f"整理 {summary.get('rule_hit_count', 0)} 条规则命中，"
                    f"输出 {summary.get('risk_clue_count', 0)} 条待复核线索。"
                )

            log_record.status = "success"
            log_record.completed_at = datetime.now(UTC)
            if not log_record.result_summary:
                log_record.result_summary = "Task executed successfully."

        except Exception as e:
            logger.error(f"Failed to execute Hermes task {config.task_type}: {e}")
            log_record.status = "failed"
            log_record.completed_at = datetime.now(UTC)
            log_record.error_trace = traceback.format_exc()

        finally:
            db.commit()


# 全局单例
hermes_scheduler = HermesScheduler()
