#!/usr/bin/python3

import gettext
import locale
import logging
import threading
import time
import os
import argparse
import sys
import json


import dbus
import dbus.mainloop.glib
import dbus.service
from gi.repository import GLib

from yumex.constants import BACKEND, LOCALEDIR
from yumex.service.data import Config, Indicator, Updates, open_yumex

gettext.install("yumex", LOCALEDIR)
locale.bindtextdomain("yumex", LOCALEDIR)
locale.textdomain("yumex")
logger = logging.getLogger("yumex_updater")

def setup_logging():
    # Setup logger
    logging.basicConfig(
        level=logging.DEBUG,
        format="(%(name)-5s) -  %(message)s",
        datefmt="%H:%M:%S",
    )

UPDATER_BUS_NAME = "dk.yumex.UpdateService"
UPDATER_OBJECT_PATH = "/" + UPDATER_BUS_NAME.replace(".", "/")


class UpdateService(dbus.service.Object):
    @dbus.service.method(dbus_interface=UPDATER_BUS_NAME, in_signature="b", out_signature="")
    def RefreshUpdates(self, refresh: bool) -> None:
        logger.debug(f"DBUS: RefreshUpdates {refresh}")
        GLib.idle_add(refresh_updates, refresh)

    @dbus.service.method(dbus_interface=UPDATER_BUS_NAME, in_signature="b", out_signature="uuu")
    def GetUpdates(self, refresh: bool) -> tuple[int, int, int]:
        updates: Updates = Updates.get_updates(refresh)
        return updates.sys_update_count, updates.flatpak_user_count, updates.flatpak_sys_count



def refresh_updates(refresh: bool) -> None:
    """check for new updates"""
    logger.debug(f"Refreshing updates ({refresh})")
    updates: Updates = Updates.get_updates(refresh)

    update_count = updates.sys_update_count + updates.flatpak_user_count + updates.flatpak_sys_count

    logger.debug(f" --> flatpak system : {updates.flatpak_sys_count}")
    logger.debug(f" --> flatpak user   : {updates.flatpak_user_count}")
    logger.debug(f" --> {BACKEND}   : {updates.sys_update_count}")

    hover_text_lines = ["There are updates available:"]
    noti_body = ""
    if updates.sys_update_count > 0:
        hover_text_lines.append(f"  System: {updates.sys_update_count}")
        noti_body += f"{updates.sys_update_count} package(s). "
    if updates.flatpak_user_count > 0:
        hover_text_lines.append(f"  Flatpak (user): {updates.flatpak_user_count}")
        noti_body += f"{updates.flatpak_user_count} flatpak(s) (user). "
    if updates.flatpak_sys_count > 0:
        hover_text_lines.append(f"  Flatpak (system): {updates.flatpak_sys_count}")
        noti_body += f"{updates.flatpak_sys_count} flatpak(s) (system). "
    hover_text = "\n".join(hover_text_lines)
    if CONFIG and CONFIG.show_icon and INDICATOR:
        if update_count > 0:
            GLib.idle_add(INDICATOR.set_title, hover_text)
            INDICATOR.last_flatpaks = updates.flatpak_user_count + updates.flatpak_sys_count
            INDICATOR.last_pkgs = updates.sys_update_count
        else:
            INDICATOR.clear()

    if CONFIG and CONFIG.send_notification and NOTIFICATION:
        logger.debug(f"Notification : {update_count=} {NOTIFICATION.last_value=} ")
        if update_count > 0 and update_count != NOTIFICATION.last_value:
            summary = _("Updates are available")
            body = noti_body
            NOTIFICATION.send(summary, body)
            NOTIFICATION.last_value = update_count
            NOTIFICATION.last_pkgs = updates.sys_update_count
            NOTIFICATION.last_flatpaks = updates.flatpak_user_count + updates.flatpak_sys_count


def check_updates() -> None:
    """Main thread function for checking for new updates at a given time periode"""
    while True:
        refresh_updates(False)
        time.sleep(CONFIG.update_sync_interval)


def setup_notification():
    """Setup the notifier class"""
    from yumex.service.notification import Action, Notification

    app_name = "Yum Extender"
    icon_name = "software-update-available-symbolic"
    # action to show in notification
    actions = [Action(id="open-yumex", title="Open Yum Extender", callback=open_yumex)]
    notification = Notification(app_name, icon_name, actions=actions)
    return notification


# Setup Global Constants
CONFIG = None
INDICATOR = None
NOTIFICATION = None

DISALLOWED_USERS = {"liveuser", "gnome-initial-setup"}

def manual_update_check(refresh: bool, json_out: bool = False) -> int:
    """
    Check updates without running the service.
    Exit codes:
      0   = no updates
      100 = updates available
      2   = error
    """
    try:
        updates: Updates = Updates.get_updates(refresh)
        total = updates.sys_update_count + updates.flatpak_user_count + updates.flatpak_sys_count

        if json_out:
            print(json.dumps({
                "total": total,
                "system": updates.sys_update_count,
                "flatpak_user": updates.flatpak_user_count,
                "flatpak_system": updates.flatpak_sys_count,
            }))
        else:
            print(f"System: {updates.sys_update_count}")
            print(f"Flatpak (user): {updates.flatpak_user_count}")
            print(f"Flatpak (system): {updates.flatpak_sys_count}")
            print(f"Total: {total}")

        return 100 if total > 0 else 0
    except Exception as e:
        print(f"Error checking updates: {e}", file=sys.stderr)
        return 2

def main():
    global CONFIG, INDICATOR, NOTIFICATION
    setup_logging()

    CONFIG = Config.from_gsettings()

    # Only create indicator/notifications in service mode
    INDICATOR = Indicator(custom_updater=CONFIG.custom_updater, refresh_func=refresh_updates, dark_icon=CONFIG.dark_icon)

    current_user = os.getenv("USER") or os.getlogin()
    if CONFIG.send_notification and current_user not in DISALLOWED_USERS:
        try:
            NOTIFICATION = setup_notification()
        except dbus.DBusException as e:
            logger.warning(f"Notifications unavailable, continuing without them: {e}")
            NOTIFICATION = None
    else:
        NOTIFICATION = None

    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)

    session_bus = dbus.SessionBus()
    name = dbus.service.BusName(UPDATER_BUS_NAME, session_bus)  # noqa: F841
    obj = UpdateService(session_bus, UPDATER_OBJECT_PATH)  # noqa: F841

    update_thread = threading.Thread(target=check_updates, daemon=True)
    update_thread.start()
    loop = GLib.MainLoop()
    loop.run()

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--check", action="store_true",
                        help="Check for updates once and exit (works without service)")
    parser.add_argument("--refresh", action="store_true",
                        help="Force refresh metadata when used with --check")
    parser.add_argument("--json", action="store_true",
                        help="Print result as JSON when used with --check")
    args = parser.parse_args()

    if args.check:
        # IMPORTANT: do NOT touch gsettings/indicator/notifications here.
        raise SystemExit(manual_update_check(args.refresh, json_out=args.json))

    main()
