#!/usr/bin/python3
# -*- coding: utf-8 -*-

import logging
import logging.handlers
import os
import signal
import time
from pathlib import Path

import notify2  # pylint: disable=import-error

from sun.__metadata__ import __prgnam__
from sun.configs import DAEMON_PID_FILE, TRAY_PID_FILE, configs_load
from sun.fetcher import Fetcher

LOG_FILE: Path = Path.home() / '.local' / 'share' / __prgnam__ / 'sun-daemon.log'


def _setup_logger() -> logging.Logger:
    """Sets up a rotating file logger for the daemon.

    Returns:
        logging.Logger: Configured logger instance.
    """
    LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
    handler = logging.handlers.RotatingFileHandler(
        LOG_FILE, maxBytes=1024 * 1024, backupCount=2
    )
    handler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
    logger = logging.getLogger(__prgnam__)
    logger.setLevel(logging.INFO)
    logger.addHandler(handler)
    return logger


class Notifier:  # pylint: disable=[R0902]
    """Desktop notification daemon for software update alerts.

    Attributes:
        count_packages (int): Number of available software updates.
        fetch (Fetcher): Fetcher instance for retrieving package updates.
        icon (str): Path to the notification icon file.
        logger (logging.Logger): Logger instance for daemon activity.
        message (str): Notification message text.
        notify: notify2 module or active Notification object.
        title (str): Notification window title.
    """

    def __init__(self, logger: logging.Logger) -> None:
        self.configs = configs_load
        self.fetch = Fetcher()
        self.logger = logger

        self.notify = notify2
        self.message: str = ''
        self.count_packages: int = 0
        self.title: str = 'Slackware Updates'
        self.icon: str = f"{self.configs.data_configs['icon_path']}/{__prgnam__}/{__prgnam__}.png"

        try:
            notify2.uninit()
        except Exception:  # pylint: disable=broad-except
            self.logger.debug('notify2.uninit() failed (expected on first run)')
        notify2.init('sun')

    def set_notification_message(self) -> None:
        """Fetches the latest updates and prepares the dbus notification message.
        """
        self.count_packages = len(list(self.fetch.updates()))
        self.message = f'<b>{self.count_packages}</b> packages available for update'
        body_len = len(f'{self.count_packages} packages available for update')
        padding = max(0, (body_len - len(self.title)) // 2)
        centered_title = f"{'':>{padding}}{self.title}"
        self.notify = notify2.Notification(centered_title, self.message, self.icon)
        self.notify.set_timeout(60000 * self.configs.standby)
        self.notify.set_category('software')

    def _signal_tray(self) -> None:
        """Send SIGUSR1 to the tray icon process so it refreshes the badge immediately."""
        if TRAY_PID_FILE.is_file():
            try:
                tray_pid = int(TRAY_PID_FILE.read_text().strip())
                os.kill(tray_pid, signal.SIGUSR1)
                self.logger.info('Sent SIGUSR1 to tray (PID %s)', tray_pid)
            except (ValueError, ProcessLookupError, OSError):
                pass

    def run(self) -> None:
        """Runs the notification loop, checking for updates at each configured interval.
        """
        while True:
            try:
                self.set_notification_message()
                self.logger.info('Update check: %d package(s) available', self.count_packages)
                if self.count_packages > 0:
                    if self.configs.notifications:
                        self.notify.show()
                        self.logger.info('Notification shown')
                    self._signal_tray()
            except Exception as e:  # pylint: disable=broad-except
                self.logger.exception('Error during update check: %s', e)

            time.sleep(60 * self.configs.interval)


def main() -> None:
    """Starts the notifier.

    Raises:
        SystemExit: Exit code 1.
    """
    logger = _setup_logger()

    # Write PID file on startup so `sun status` works correctly when the
    # daemon is launched via XDG autostart (not through `sun start`).
    pid_file = DAEMON_PID_FILE
    pid_file.parent.mkdir(exist_ok=True)
    pid_file.write_text(str(os.getpid()))

    logger.info('sun-daemon started (PID %s)', os.getpid())
    try:
        notifier = Notifier(logger)
        notifier.run()
    except KeyboardInterrupt as e:
        logger.info('sun-daemon stopped')
        raise SystemExit(1) from e
    except Exception as e:  # pylint: disable=broad-except
        logger.error('sun-daemon crashed: %s', e, exc_info=True)
        raise SystemExit(1) from e
    finally:
        pid_file.unlink(missing_ok=True)
