from __future__ import annotations

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

from sqlalchemy import select

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.digital_employee_reminder_task import DigitalEmployeeReminderTaskService

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


class DigitalEmployeeReminderScheduler:
    def __init__(self) -> None:
        timezone_name = str(os.environ.get("X_FINANCIAL_SCHEDULER_TZ") or "Asia/Shanghai").strip()
        reminder_time = str(os.environ.get("X_FINANCIAL_REMINDER_SCAN_TIME") or "02:00").strip()
        initial_delay = int(os.environ.get("X_FINANCIAL_REMINDER_INITIAL_DELAY_SECONDS") or "24")
        self._timezone = ZoneInfo(timezone_name or "Asia/Shanghai")
        self._scan_time = self._parse_scan_time(reminder_time)
        self._initial_delay_seconds = max(1, initial_delay)
        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="digital-employee-reminder-scheduler",
                daemon=True,
            )
            self._thread.start()
            logger.info(
                "Digital employee reminder scheduler started timezone=%s scan_time=%s",
                self._timezone.key,
                self._scan_time.strftime("%H:%M"),
            )

    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("Digital employee reminder scheduler stopped")

    def _run_loop(self) -> None:
        if self._stop_event.wait(self._initial_delay_seconds):
            return
        self._refresh_reminders(reason="startup_warmup")
        while not self._stop_event.is_set():
            wait_seconds = self._seconds_until_next_scan()
            if self._stop_event.wait(wait_seconds):
                break
            self._refresh_reminders(reason="scheduled_0200")

    def _refresh_reminders(self, *, reason: str) -> None:
        db = get_session_factory()()
        try:
            tenant_ids = list(
                db.scalars(
                    select(Tenant.tenant_id)
                    .where(Tenant.status == "active")
                    .order_by(Tenant.tenant_id.asc())
                ).all()
            )
            for tenant_id in tenant_ids:
                result = DigitalEmployeeReminderTaskService(
                    db,
                    tenant_id=tenant_id,
                ).refresh_reminders(source=AgentRunSource.SCHEDULE.value)
                summary = result.get("summary") or {}
                logger.info(
                    "Digital employee reminder scan generated tenant=%s "
                    "reason=%s recipients=%s reminders=%s",
                    tenant_id,
                    reason,
                    summary.get("recipient_count"),
                    summary.get("reminder_count"),
                )
        except Exception:
            db.rollback()
            logger.exception("Scheduled digital employee reminder scan failed")
        finally:
            db.close()

    def _seconds_until_next_scan(self) -> float:
        now = datetime.now(self._timezone)
        target = datetime.combine(now.date(), self._scan_time, tzinfo=self._timezone)
        if target <= now:
            target = target + timedelta(days=1)
        return max(1.0, (target - now).total_seconds())

    @staticmethod
    def _parse_scan_time(raw_value: str) -> time:
        value = str(raw_value or "").strip()
        try:
            hour_text, minute_text = value.split(":", 1)
            hour = min(max(int(hour_text), 0), 23)
            minute = min(max(int(minute_text), 0), 59)
            return time(hour=hour, minute=minute)
        except Exception:
            return time(hour=2, minute=0)


digital_employee_reminder_scheduler = DigitalEmployeeReminderScheduler()
