"""
알림 오케스트레이션.

5분 가드로 건강/주간/누적 임계 알림을 throttle.
notifier.py의 알림 메서드를 적절한 시점에 호출.
"""
from __future__ import annotations
from datetime import datetime

from core.settings_keys import (
    HEALTH_CONSECUTIVE_OT_DAYS, WEEKLY_HOURS_THRESHOLD, OVERTIME_THRESHOLD_HOURS,
)
from core.i18n import tr
from utils.debug_log import dlog


def _get_int(db, key: str, default: int, lo: int, hi: int) -> int:
    try:
        v = int(db.get_setting(key, str(default)) or default)
    except (ValueError, TypeError):
        v = default
    return max(lo, min(hi, v))


class NotificationOrchestrator:
    """update_display() 1Hz tick에서 호출."""

    def __init__(self, window):
        self.window = window
        self.db = window.db
        self.notifier = window.notifier
        self._last_5min_bucket: int | None = None  # now.minute (5의 배수일 때만)

    def maybe_send_weekly_report(self, now: datetime) -> None:
        """월요일 첫 update_display 호출 시 지난주 요약 발송 (시스템 + Discord).

        notification_log로 중복 가드. 월요일이 아니거나 이미 보냈으면 no-op.
        """
        if now.weekday() != 0:  # 0=월요일
            return
        if self.db.has_notification_today('system', 'weekly_report'):
            return
        # 지난주 데이터 (월~일)
        from datetime import timedelta as _td
        last_mon = now.date() - _td(days=7)
        last_sun = now.date() - _td(days=1)
        records = self.db.get_work_records_by_range(last_mon.isoformat(), last_sun.isoformat())
        closed = [r for r in records if r.get('clock_out')]
        if not closed:
            return  # 지난주 기록 없음
        total_h = sum((r.get('total_hours') or 0) for r in closed)
        ot_total = sum((r.get('overtime_minutes') or 0) for r in closed)
        ot_h, ot_m = ot_total // 60, ot_total % 60
        avg_h = total_h / len(closed) if closed else 0
        longest = max(closed, key=lambda r: r.get('total_hours') or 0)
        longest_str = f"{longest['date']} ({longest.get('total_hours', 0):.1f}h)"

        title = tr('notif.weekly_report.title')
        body = tr('notif.weekly_report.body',
                  start=last_mon, end=last_sun,
                  total_h=total_h, days=len(closed),
                  avg_h=avg_h, ot_h=ot_h, ot_m=ot_m,
                  longest=longest_str)
        self.notifier.notification_signal.emit(title, body)
        self.db.log_notification('system', 'weekly_report')

        # Discord 도 옵션 활성 시 push
        if self.db.get_setting('discord_notif_clock_out', 'true').lower() == 'true':
            url = self.db.get_setting('discord_webhook_url', '') or ''
            if url:
                try:
                    from utils.discord_webhook import send, COLOR_BLUE
                    fields = [
                        {"name": tr('field.total_work'), "value": tr('field.total_work_value', hours=total_h, days=len(closed)), "inline": True},
                        {"name": tr('field.avg_daily'), "value": tr('field.avg_daily_value', hours=avg_h), "inline": True},
                        {"name": tr('field.overtime'), "value": tr('field.overtime_value', hours=ot_h, minutes=ot_m), "inline": True},
                        {"name": tr('field.longest_day'), "value": longest_str, "inline": False},
                    ]
                    ok = send(url, tr('notif.weekly_report.title'),
                               tr('notif.weekly_report.body',
                                  start=last_mon, end=last_sun,
                                  total_h=total_h, days=len(closed),
                                  avg_h=avg_h, ot_h=ot_h, ot_m=ot_m,
                                  longest=longest_str),
                               color=COLOR_BLUE, fields=fields)
                    self.db.log_notification('discord', 'weekly_report', success=ok)
                except Exception as e:
                    dlog(f"discord weekly_report failed: {e}")

    def tick(self, now: datetime, expected_clock_out: datetime, remaining_seconds: float,
              is_holiday: bool = False) -> None:
        n = self.notifier
        # "퇴근 30분 전" 알림은 휴일/주말엔 무의미 (정해진 퇴근시각 없음)
        if not is_holiday:
            n.check_clock_out_soon(expected_clock_out, now)
        # 점심/저녁/장시간 휴식 알림은 휴일에도 그대로 — 식사·건강은 휴일에도 챙김
        n.check_lunch_reminder(self.window.clock_in_time,
                               self.window.lunch_break_enabled, now)
        n.check_dinner_reminder(self.window.clock_in_time,
                                 getattr(self.window, 'dinner_break_enabled', False), now)
        if remaining_seconds < 0:
            n.check_overtime_earning(abs(int(remaining_seconds / 60)))

        # 5분 간격 throttle: 건강/주간/누적/휴식권고/주간리포트
        # 임계값 가드(>=3, >52, >=1200)는 notifier 내부에서 설정값으로 재검사하므로
        # 여기서는 항상 호출 — 설정 변경이 즉시 반영되도록.
        if now.minute % 5 == 0 and self._last_5min_bucket != now.minute:
            self._last_5min_bucket = now.minute

            # 월요일 첫 출근 시 지난주 리포트
            self.maybe_send_weekly_report(now)

            # 휴식 권고 (장시간 연속 근무)
            break_minutes = self.db.get_total_break_minutes_today()
            n.check_health_break(self.window.clock_in_time, break_minutes, now)

            # 임계값은 한 번만 읽어 시스템 알림과 Discord push에 동일 적용
            consecutive = self.db.get_consecutive_overtime_days()
            consecutive_th = _get_int(self.db, HEALTH_CONSECUTIVE_OT_DAYS, 3, 1, 14)
            if consecutive >= consecutive_th:
                n.notify_health_warning(consecutive)
                self._discord_health(consecutive, break_minutes, now)

            weekly_hours = self.db.get_weekly_stats().get('total_hours', 0)
            weekly_th = _get_int(self.db, WEEKLY_HOURS_THRESHOLD, 52, 20, 168)
            if weekly_hours > weekly_th:
                n.notify_weekly_hours(weekly_hours)

            balance_minutes = self.db.get_total_overtime_balance()
            ot_threshold_h = _get_int(self.db, OVERTIME_THRESHOLD_HOURS, 20, 1, 200)
            if balance_minutes / 60.0 >= ot_threshold_h:
                n.notify_overtime_threshold(balance_minutes / 60.0)

            # 도전과제 평가 (5분 throttle)
            self._evaluate_achievements(now)

    def _evaluate_achievements(self, now: datetime) -> None:
        """도전과제 평가 + 신규 잠금 해제 알림.

        실패는 silent — 도전과제 시스템이 메인 흐름을 막으면 안 됨.
        """
        try:
            from core.achievements import evaluate_all
            unlocked = evaluate_all(self.db)
        except Exception as e:
            dlog(f"achievement eval failed: {e}")
            return
        if not unlocked:
            return
        # 시스템 알림 + Discord push (옵션)
        notif_on = self.db.get_setting('notification_achievement', 'true').lower() == 'true'
        for a in unlocked:
            self.db.log_notification('system', f'achievement:{a.code}')
            if notif_on:
                title = tr('notif.achievement.title', icon=a.badge_icon)
                body = tr('notif.achievement.body', name=a.name, description=a.description)
                self.notifier.notification_signal.emit(title, body)
        # Discord 통합 push (여러 개면 묶어서)
        self._discord_achievements(unlocked)

    def _discord_achievements(self, unlocked: list) -> None:
        if self.db.get_setting('discord_notif_achievement', 'true').lower() != 'true':
            return
        url = self.db.get_setting('discord_webhook_url', '') or ''
        if not url:
            return
        try:
            from utils import discord_webhook
            fields = [{"name": f"{a.badge_icon} {a.name}",
                        "value": a.description, "inline": False}
                       for a in unlocked[:10]]
            extra = (f"\n... 외 {len(unlocked) - 10}개" if len(unlocked) > 10 else '')
            ok = discord_webhook.send(
                url,
                tr('discord.achievement.title', count=len(unlocked)),
                tr('discord.achievement.body', extra=extra),
                color=discord_webhook.COLOR_YELLOW,
                fields=fields,
            )
            self.db.log_notification('discord', 'achievement', success=ok)
        except Exception as e:
            dlog(f"discord achievement push failed: {e}")

    def _discord_health(self, days: int, break_minutes: int, now: datetime) -> None:
        """건강 경고 Discord push (옵션)."""
        if self.db.has_notification_today('discord', 'health'):
            return
        if self.db.get_setting('discord_notif_health', 'true').lower() != 'true':
            return
        url = self.db.get_setting('discord_webhook_url', '') or ''
        if not url:
            return
        try:
            from utils import discord_webhook
            elapsed = (now - self.window.clock_in_time).total_seconds() / 3600 - break_minutes / 60
            ok = discord_webhook.send_health_warning(url, elapsed)
            self.db.log_notification('discord', 'health', success=ok)
        except Exception as e:
            dlog(f"discord health push failed: {e}")
