from __future__ import annotations

import os
import threading
from datetime import datetime, time, timedelta
from zoneinfo import ZoneInfo

from sqlalchemy import select

from app.api.deps import CurrentUserContext
from app.core.agent_enums import AgentRunSource
from app.core.logging import get_logger
from app.db.session import get_session_factory
from app.models.tenant import Tenant
from app.services.knowledge_sync import KnowledgeSyncDispatchService

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


class KnowledgeIndexScheduler:
    def __init__(self) -> None:
        timezone_name = (
            str(os.environ.get("X_FINANCIAL_SCHEDULER_TZ") or "Asia/Shanghai").strip()
            or "Asia/Shanghai"
        )
        self._timezone = ZoneInfo(timezone_name)
        self._stop_event = threading.Event()
        self._thread: threading.Thread | None = None
        self._lock = threading.Lock()

    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="knowledge-index-scheduler",
                daemon=True,
            )
            self._thread.start()
            logger.info(
                "Knowledge index scheduler started timezone=%s trigger=00:00",
                self._timezone.key,
            )

    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("Knowledge index scheduler stopped")

    def _run_loop(self) -> None:
        while not self._stop_event.is_set():
            now = datetime.now(self._timezone)
            next_run = self._resolve_next_run(now)
            wait_seconds = max(1.0, (next_run - now).total_seconds())
            if self._stop_event.wait(wait_seconds):
                break
            try:
                self._run_incremental_sync()
            except Exception:  # pragma: no cover - scheduler best effort logging
                logger.exception("Scheduled knowledge index sync failed")

    def _run_incremental_sync(self) -> None:
        db = get_session_factory()()
        try:
            tenants = list(
                db.scalars(
                    select(Tenant)
                    .where(Tenant.status == "active")
                    .order_by(Tenant.tenant_id.asc())
                )
            )
            if not tenants:
                logger.warning(
                    "Scheduled knowledge index sync skipped: no active tenant registry rows"
                )
                return
            for tenant in tenants:
                current_user = CurrentUserContext(
                    username="hermes",
                    name="Hermes",
                    role_codes=["manager"],
                    is_admin=True,
                    tenant_id=tenant.tenant_id,
                )
                try:
                    result = KnowledgeSyncDispatchService(db).queue_sync(
                        current_user=current_user,
                        folder=None,
                        document_ids=None,
                        source=AgentRunSource.SCHEDULE.value,
                        force=False,
                        changed_only=True,
                    )
                    logger.info(
                        "Scheduled knowledge index sync result tenant=%s run_id=%s "
                        "docs=%s reused=%s summary=%s",
                        tenant.tenant_id,
                        result.agent_run_id,
                        len(result.document_ids),
                        result.reused,
                        result.summary,
                    )
                except Exception:
                    db.rollback()
                    logger.exception(
                        "Scheduled knowledge index sync failed tenant=%s",
                        tenant.tenant_id,
                    )
        finally:
            db.close()

    @staticmethod
    def _resolve_next_run(now: datetime) -> datetime:
        today_midnight = datetime.combine(now.date(), time(hour=0, minute=0), tzinfo=now.tzinfo)
        if now < today_midnight:
            return today_midnight
        return today_midnight + timedelta(days=1)


knowledge_index_scheduler = KnowledgeIndexScheduler()
