"""
Daily reminder scheduler.

Runs every minute. For each user with an enabled reminder:
  - Converts current UTC time to the user's local timezone
  - Checks if the current HH:MM matches their reminder time
  - Checks if they already got a notification today (avoids duplicates)
  - Checks if they have already written a journal entry today
  - If not, sends an FCM push notification to all their registered devices
"""
import json
import logging
from datetime import datetime, timedelta

import pytz
import firebase_admin
from firebase_admin import credentials, messaging
from apscheduler.schedulers.background import BackgroundScheduler

from config import get_settings
from db import get_database

log = logging.getLogger(__name__)

_firebase_initialized = False


def init_firebase():
    """Initialize Firebase Admin SDK once using the service account JSON from env."""
    global _firebase_initialized
    if _firebase_initialized:
        return

    settings = get_settings()
    if not settings.firebase_service_account_json:
        log.warning("FIREBASE_SERVICE_ACCOUNT_JSON not set — push notifications disabled")
        return

    try:
        sa_dict = json.loads(settings.firebase_service_account_json)
        cred = credentials.Certificate(sa_dict)
        firebase_admin.initialize_app(cred)
        _firebase_initialized = True
        log.info("Firebase Admin SDK initialized")
    except Exception as e:
        log.error(f"Failed to initialize Firebase Admin SDK: {e}")


def send_reminder_notifications():
    """Check all users and send reminders where due."""
    if not _firebase_initialized:
        log.warning("Reminder check skipped — Firebase not initialized")
        return

    db = get_database()
    now_utc = datetime.utcnow().replace(second=0, microsecond=0)

    candidates = list(db.users.find({
        "reminder.enabled": True,
        "fcmTokens": {"$exists": True, "$not": {"$size": 0}},
    }))

    log.debug(f"Reminder check at {now_utc.strftime('%H:%M')} UTC — {len(candidates)} candidate(s)")

    for user in candidates:
        try:
            if user.get("reminder", {}).get("time"):
                _process_user(db, user, now_utc)
            _process_universal(db, user, now_utc)
        except Exception as e:
            log.error(f"Error processing reminder for user {user.get('_id')}: {e}")


def _get_user_local_time(now_utc: datetime, timezone_str: str):
    """Returns (now_local, today_str, user_tz)."""
    try:
        user_tz = pytz.timezone(timezone_str)
    except pytz.UnknownTimeZoneError:
        user_tz = pytz.utc
    now_local = now_utc.replace(tzinfo=pytz.utc).astimezone(user_tz)
    today_str = now_local.strftime("%Y-%m-%d")
    return now_local, today_str, user_tz


def _wrote_today(db, user_id, now_local, user_tz) -> bool:
    today_start_local = now_local.replace(hour=0, minute=0, second=0, microsecond=0)
    today_start_utc = today_start_local.astimezone(pytz.utc).replace(tzinfo=None)
    today_end_utc = today_start_utc + timedelta(days=1)
    return db.entries.count_documents({
        "userId": user_id,
        "createdAt": {"$gte": today_start_utc, "$lt": today_end_utc},
    }) > 0


def _process_user(db, user: dict, now_utc: datetime):
    uid = user.get("_id")
    reminder = user.get("reminder", {})
    reminder_time_str = reminder.get("time")
    timezone_str = reminder.get("timezone", "UTC")
    fcm_tokens: list = user.get("fcmTokens", [])

    if not reminder_time_str or not fcm_tokens:
        return

    now_local, today_str, user_tz = _get_user_local_time(now_utc, timezone_str)
    current_hm = now_local.strftime("%H:%M")

    if current_hm != reminder_time_str:
        log.debug(f"User {uid}: skipped — current time {current_hm} != reminder time {reminder_time_str} ({timezone_str})")
        return

    if _wrote_today(db, uid, now_local, user_tz):
        log.debug(f"User {uid}: skipped — already wrote today")
        return

    log.info(f"User {uid}: sending reminder (time={reminder_time_str}, tz={timezone_str})")
    _send_push(uid, fcm_tokens, db)


def _process_universal(db, user: dict, now_utc: datetime):
    """Universal 11pm reminder — fires if enabled and no entry written today."""
    uid = user.get("_id")
    reminder = user.get("reminder", {})
    timezone_str = reminder.get("timezone", "UTC")
    fcm_tokens: list = user.get("fcmTokens", [])

    if not fcm_tokens:
        return

    now_local, today_str, user_tz = _get_user_local_time(now_utc, timezone_str)

    if now_local.strftime("%H:%M") != "23:00":
        return

    if reminder.get("lastUniversalDate") == today_str:
        log.debug(f"User {uid}: universal reminder skipped — already sent today")
        return

    if _wrote_today(db, uid, now_local, user_tz):
        log.debug(f"User {uid}: universal reminder skipped — already wrote today")
        db.users.update_one({"_id": uid}, {"$set": {"reminder.lastUniversalDate": today_str}})
        return

    log.info(f"User {uid}: sending universal 11pm reminder (tz={timezone_str})")
    _send_push(uid, fcm_tokens, db, universal=True)
    db.users.update_one({"_id": uid}, {"$set": {"reminder.lastUniversalDate": today_str}})


def _send_push(user_id, tokens: list, db, universal: bool = False):
    """Send FCM multicast and prune stale tokens."""
    title = "Last chance to journal today 🌙" if universal else "Time to journal 🌱"
    message = messaging.MulticastMessage(
        notification=messaging.Notification(
            title=title,
            body="You haven't written today yet. Take a moment to reflect.",
        ),
        tokens=tokens,
        android=messaging.AndroidConfig(priority="high"),
        apns=messaging.APNSConfig(
            payload=messaging.APNSPayload(
                aps=messaging.Aps(sound="default")
            )
        ),
        webpush=messaging.WebpushConfig(
            notification=messaging.WebpushNotification(
                icon="/web-app-manifest-192x192.png",
                badge="/favicon-96x96.png",
                tag="gj-daily-reminder",
            )
        ),
    )

    response = messaging.send_each_for_multicast(message)
    log.info(f"Reminder sent to user {user_id}: {response.success_count} ok, {response.failure_count} failed")

    stale_tokens = [
        tokens[i] for i, r in enumerate(response.responses)
        if not r.success and r.exception and "not-registered" in str(r.exception).lower()
    ]
    if stale_tokens:
        db.users.update_one(
            {"_id": user_id},
            {"$pullAll": {"fcmTokens": stale_tokens}}
        )
        log.info(f"Removed {len(stale_tokens)} stale FCM tokens for user {user_id}")


def start_scheduler() -> BackgroundScheduler:
    """Initialize Firebase and start the minute-by-minute scheduler."""
    init_firebase()

    scheduler = BackgroundScheduler(timezone="UTC")
    scheduler.add_job(
        send_reminder_notifications,
        trigger="cron",
        minute="*",   # every minute
        id="daily_reminders",
        replace_existing=True,
    )
    scheduler.start()
    log.info("Reminder scheduler started")
    return scheduler
