#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>

#include "../include/chroma.h"

// Global state for signal handling
volatile sig_atomic_t chroma_should_quit = 0;

static void print_usage(const char *program_name) {
  printf("Usage: %s [OPTIONS]\n", program_name);
  printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n\n");
  printf("Options:\n");
  printf("  -c, --config FILE    Configuration file path\n");
  printf("  -d, --daemon         Run as daemon\n");
  printf("  -v, --verbose        Increase verbosity (can be used multiple "
         "times)\n");
  printf("                       -v: INFO, -vv: DEBUG, -vvv: TRACE\n");
  printf("  -h, --help          Show this help\n");
  printf("  --version           Show version information\n");
  printf("\nExamples:\n");
  printf("  %s -c ~/.config/chroma/chroma.toml\n", program_name);
  printf("  %s --daemon\n", program_name);
}

static void print_version(void) {
  printf("chroma %s\n", CHROMA_VERSION);
  printf("Minimal Wayland Multi-Monitor Wallpaper Daemon\n");
}

static int daemonize(void) {
  pid_t pid = fork();

  if (pid < 0) {
    chroma_log("ERROR", "fork: %s", strerror(errno));
    return -1;
  }

  if (pid > 0) {
    // Parent process exits
    exit(0);
  }

  // Child process continues
  if (setsid() < 0) {
    chroma_log("ERROR", "setsid: %s", strerror(errno));
    return -1;
  }

  // Change working directory to root
  if (chdir("/") < 0) {
    chroma_log("ERROR", "chdir: %s", strerror(errno));
    return -1;
  }

  // Close standard file descriptors
  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);

  return 0;
}

static char *get_default_config_path(void) {
  static char config_path[MAX_PATH_LEN];
  const char *home = getenv("HOME");
  const char *xdg_config = getenv("XDG_CONFIG_HOME");

  if (xdg_config) {
    snprintf(config_path, sizeof(config_path), "%s/chroma/%s", xdg_config,
             CONFIG_FILE_NAME);
  } else if (home) {
    snprintf(config_path, sizeof(config_path), "%s/.config/chroma/%s", home,
             CONFIG_FILE_NAME);
  } else {
    strcpy(config_path, CONFIG_FILE_NAME);
  }

  return config_path;
}

int main(int argc, char *argv[]) {
  chroma_state_t state;
  char *config_file = NULL;
  bool daemon_mode = false;
  int verbose_level = 0;
  int opt;
  int ret = 0;

  static struct option long_options[] = {
      {"config", required_argument, 0, 'c'}, {"daemon", no_argument, 0, 'd'},
      {"verbose", no_argument, 0, 'v'},      {"help", no_argument, 0, 'h'},
      {"version", no_argument, 0, 'V'},      {0, 0, 0, 0}};

  // Parse command line arguments
  while ((opt = getopt_long(argc, argv, "c:dvhV", long_options, NULL)) != -1) {
    switch (opt) {
    case 'c':
      config_file = optarg;
      break;
    case 'd':
      daemon_mode = true;
      break;
    case 'v':
      verbose_level++;
      break;
    case 'h':
      print_usage(argv[0]);
      return 0;
    case 'V':
      print_version();
      return 0;
    default:
      print_usage(argv[0]);
      return 1;
    }
  }

  // Initialize state
  memset(&state, 0, sizeof(state));
  state.config.daemon_mode = daemon_mode;

  // Set log level based on verbosity count
  // 0: ERROR+WARN only, 1: +INFO, 2: +DEBUG, 3+: +TRACE
  chroma_set_log_level(verbose_level);

  // Load configuration
  if (!config_file) {
    config_file = get_default_config_path();
  }

  // Store config path in state for IPC reload
  strncpy(state.config_file, config_file, MAX_PATH_LEN - 1);
  state.config_file[MAX_PATH_LEN - 1] = '\0';

  // Daemonize if requested
  if (daemon_mode) {
    chroma_log("INFO", "Starting daemon mode");
    if (daemonize() != 0) {
      fprintf(stderr, "Failed to daemonize\n");
      return 1;
    }
  }

  // Initialize chroma
  chroma_log("INFO", "Initializing chroma wallpaper daemon v%s",
             CHROMA_VERSION);
  chroma_log_memory_stats("startup");

  ret = chroma_init(&state);
  if (ret != CHROMA_OK) {
    chroma_log("ERROR", "Failed to initialize chroma: %s",
               chroma_error_string(ret));
    chroma_cleanup(&state);
    return 1;
  }
  chroma_log_memory_stats("post-init");

  // Set up signal handlers after state is initialized
  chroma_set_signal_state(&state, config_file);
  chroma_handle_signals();

  // Load configuration
  chroma_log("INFO", "Loading configuration from: %s", config_file);
  if (chroma_config_load(&state.config, config_file) != CHROMA_OK) {
    chroma_log("WARN", "Failed to load config file, using defaults");
  }
  chroma_log_memory_stats("post-config-load");

  // Connect to Wayland
  ret = chroma_wayland_connect(&state);
  if (ret != CHROMA_OK) {
    chroma_log("ERROR", "Failed to connect to Wayland: %s",
               chroma_error_string(ret));
    chroma_cleanup(&state);
    return 1;
  }
  chroma_log_memory_stats("post-wayland-connect");

  // Initialize IPC socket for runtime commands
  if (chroma_ipc_init(&state) != CHROMA_OK) {
    chroma_log("WARN", "Failed to initialize IPC socket");
  }

  chroma_log("INFO", "Chroma daemon initialized successfully");
  chroma_log_memory_stats("pre-main-loop");

  // Main event loop
  ret = chroma_run(&state);
  if (ret != CHROMA_OK) {
    chroma_log("ERROR", "Main loop failed: %s", chroma_error_string(ret));
  }
  chroma_log_memory_stats("post-main-loop");

  // Cleanup
  chroma_log("INFO", "Shutting down chroma daemon");
  chroma_log_memory_stats("pre-cleanup");
  chroma_cleanup(&state);
  chroma_log_memory_stats("post-cleanup");

  return (ret == CHROMA_OK) ? 0 : 1;
}
