// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "netlink_client.h"

#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void print_usage(const char *prog)
{
    printf("Usage: %s [OPTIONS]\n", prog);
    printf("Monitor read-only filesystem error messages from the /usr directory.\n\n");
    printf("OPTIONS:\n");
    printf("  -h, --help       Show this help message and exit\n");
    printf("  --enable         Enable error notification via netlink\n");
    printf("  --disable        Disable error notification via netlink\n");
    printf("  --status         Query error notification status\n");
    printf("  --verbose        Print detailed notification information\n\n");
    printf("EXAMPLES:\n");
    printf("  # Enable error notification\n");
    printf("  sudo %s --enable\n\n", prog);
    printf("  # Check status\n");
    printf("  %s --status\n\n", prog);
    printf("  # Start monitoring (default behavior)\n");
    printf("  %s\n\n", prog);
    printf("  # Start monitoring with verbose output\n");
    printf("  %s --verbose\n", prog);
}

/* Callback for netlink error notifications */
static void error_notification_callback(const struct err_notify_data *data, void *userdata)
{
    int i;

    (void)userdata;

    if (!data)
        return;

    if (data->pid <= 0) {
        fprintf(stderr, "Invalid pid in callback: %d\n", data->pid);
        return;
    }

    /* Data acquisition section */
    char cgroup_buffer[PATH_MAX] = { 0 };
    get_process_cgroup(data->pid, cgroup_buffer, sizeof(cgroup_buffer));

    char cmdline_buffer[256] = { 0 };
    get_process_cmdline(data->pid, cmdline_buffer, sizeof(cmdline_buffer), data->comm);

    printf("=== Error Notification ===\n");
    printf("PID: %d\n", data->pid);
    printf("PPID: %d\n", data->ppid);
    printf("Comm: %s\n", data->comm ? data->comm : "(null)");
    printf("Cmdline: %s\n", cmdline_buffer[0] ? cmdline_buffer : "(null)");
    printf("Cgroup: %s\n", cgroup_buffer[0] ? cgroup_buffer : "(null)");
    printf("UID: %u\n", data->uid);
    printf("GID: %u\n", data->gid);
    printf("Inode Count: %d\n", data->inode_count);

    if (data->inode_count > 0) {
        printf("Files:\n");
        for (i = 0; i < data->inode_count; i++) {
            printf("  [%d] Filename: %s\n",
                   i,
                   data->filenames && data->filenames[i] ? data->filenames[i] : "(null)");
            printf("      Inode: %lu\n", data->inodes ? data->inodes[i] : 0);
            printf("      Is Parent: %s\n",
                   data->inode_parents && data->inode_parents[i] ? "true" : "false");
        }
    }

    printf("==========================\n\n");
}

int main(int argc, char *argv[])
{
    struct err_notify_state *state = NULL;
    int ret = 0;
    bool opt_enable = false;
    bool opt_disable = false;
    bool opt_status = false;
    bool opt_verbose = false;
    int i;

    /* Parse command line arguments */
    for (i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        } else if (strcmp(argv[i], "--enable") == 0) {
            opt_enable = true;
        } else if (strcmp(argv[i], "--disable") == 0) {
            opt_disable = true;
        } else if (strcmp(argv[i], "--status") == 0) {
            opt_status = true;
        } else if (strcmp(argv[i], "--verbose") == 0) {
            opt_verbose = true;
        } else {
            fprintf(stderr, "Error: Unknown option: %s\n\n", argv[i]);
            print_usage(argv[0]);
            return 1;
        }
    }

    /* Check for conflicting options */
    if ((opt_enable && opt_disable) || (opt_enable && opt_status) || (opt_disable && opt_status)) {
        fprintf(stderr, "Error: --enable, --disable, and --status are mutually exclusive\n\n");
        print_usage(argv[0]);
        return 1;
    }

    /* Set verbose mode */
    err_notify_set_verbose(opt_verbose);

    /* Initialize Generic Netlink */
    if (err_notify_init(&state) < 0) {
        fprintf(stderr, "failed to initialize Generic Netlink\n");
        return 1;
    }

    /* Execute command based on options */
    if (opt_enable) {
        ret = err_notify_enable(state);
    } else if (opt_disable) {
        ret = err_notify_disable(state);
    } else if (opt_status) {
        ret = err_notify_get_status(state);
    } else {
        /* Default: start monitoring notifications */
        /* Register callback for error notifications */
        err_notify_set_callback(state, error_notification_callback, NULL);
        printf("Starting notification monitor...\n");
        ret = err_notify_monitor(state);
        printf("Monitoring stopped.\n");
    }

    /* Clean up */
    err_notify_cleanup(state);
    return ret;
}