#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

// Default configuration values

static char *trim_whitespace(char *str) {
  char *end;

  // Trim leading whitespace
  while (isspace((unsigned char)*str))
    str++;

  // All spaces?
  if (*str == 0)
    return str;

  // Trim trailing whitespace
  end = str + strlen(str) - 1;
  while (end > str && isspace((unsigned char)*end))
    end--;

  *(end + 1) = '\0';
  return str;
}

// Remove quotes from a string
static char *remove_quotes(char *str) {
  size_t len = strlen(str);
  if (len >= 2 && ((str[0] == '"' && str[len - 1] == '"') ||
                   (str[0] == '\'' && str[len - 1] == '\''))) {
    str[len - 1] = '\0';
    return str + 1;
  }
  return str;
}

// Parse boolean value from string
static bool parse_bool(const char *value) {
  if (!value)
    return false;

  if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 ||
      strcasecmp(value, "1") == 0 || strcasecmp(value, "on") == 0) {
    return true;
  }

  return false;
}

// Parse integer value from string

// Output-to-image mapping
static int add_output_mapping(chroma_config_t *config, const char *output_name,
                              const char *image_path) {
  if (!config || !output_name || !image_path) {
    return CHROMA_ERROR_INIT;
  }

  if (config->mapping_count >= MAX_OUTPUTS) {
    chroma_log("ERROR", "Maximum number of output mappings reached (%d)",
               MAX_OUTPUTS);
    return CHROMA_ERROR_MEMORY;
  }

  chroma_config_mapping_t *mapping = &config->mappings[config->mapping_count];

  strncpy(mapping->output_name, output_name, sizeof(mapping->output_name) - 1);
  mapping->output_name[sizeof(mapping->output_name) - 1] = '\0';

  strncpy(mapping->image_path, image_path, sizeof(mapping->image_path) - 1);
  mapping->image_path[sizeof(mapping->image_path) - 1] = '\0';

  config->mapping_count++;

  chroma_log("DEBUG", "Added mapping: %s -> %s", output_name, image_path);
  return CHROMA_OK;
}

// Initialize configuration with defaults
static void init_default_config(chroma_config_t *config) {
  if (!config)
    return;

  memset(config, 0, sizeof(chroma_config_t));

  config->daemon_mode = false;
  config->mapping_count = 0;

  // Set default image path (can be overridden)
  const char *home = getenv("HOME");
  if (home) {
    snprintf(config->default_image, sizeof(config->default_image),
             "%s/.config/chroma/default.jpg", home);
  } else {
    strcpy(config->default_image, "/usr/share/pixmaps/chroma-default.jpg");
  }
}

// Parse a single configuration line
static int parse_config_line(chroma_config_t *config, char *line,
                             int line_number) {
  if (!config || !line) {
    return CHROMA_ERROR_INIT;
  }

  // Skip empty lines and comments
  char *trimmed = trim_whitespace(line);
  if (*trimmed == '\0' || *trimmed == '#' || *trimmed == ';') {
    return CHROMA_OK;
  }

  // Find the equals sign
  char *equals = strchr(trimmed, '=');
  if (!equals) {
    chroma_log("WARN", "Invalid config line %d: no '=' found", line_number);
    return CHROMA_OK; // continue parsing
  }

  *equals = '\0';
  char *key = trim_whitespace(trimmed);
  char *value = trim_whitespace(equals + 1);

  value = remove_quotes(value);

  if (*key == '\0' || *value == '\0') {
    chroma_log("WARN", "Invalid config line %d: empty key or value",
               line_number);
    return CHROMA_OK;
  }

  // Parse configuration options
  if (strcasecmp(key, "default_image") == 0) {
    strncpy(config->default_image, value, sizeof(config->default_image) - 1);
    config->default_image[sizeof(config->default_image) - 1] = '\0';
    chroma_log("DEBUG", "Set default image: %s", value);
  } else if (strcasecmp(key, "daemon") == 0 ||
             strcasecmp(key, "daemon_mode") == 0) {
    config->daemon_mode = parse_bool(value);
    chroma_log("DEBUG", "Set daemon mode: %s",
               config->daemon_mode ? "true" : "false");
  } else if (strncasecmp(key, "output.", 7) == 0) {
    // Output-specific mapping: e.g., output.DP-1=/path/to/image.jpg
    const char *output_name = key + 7;
    if (*output_name == '\0') {
      chroma_log("WARN", "Invalid output mapping line %d: no output name",
                 line_number);
      return CHROMA_OK;
    }

    // Validate image path
    if (chroma_image_validate(value) != CHROMA_OK) {
      chroma_log("WARN", "Invalid image path for output %s: %s", output_name,
                 value);
      return CHROMA_OK;
    }

    if (add_output_mapping(config, output_name, value) != CHROMA_OK) {
      chroma_log("ERROR", "Failed to add output mapping: %s -> %s", output_name,
                 value);
      return CHROMA_ERROR_CONFIG;
    }
  } else {
    chroma_log("WARN", "Unknown configuration key line %d: %s", line_number,
               key);
  }

  return CHROMA_OK;
}

// Load configuration from file
int chroma_config_load(chroma_config_t *config, const char *config_file) {
  if (!config) {
    return CHROMA_ERROR_INIT;
  }

  // Initialize with defaults
  init_default_config(config);

  if (!config_file) {
    chroma_log("INFO", "No config file specified, using defaults");
    return CHROMA_OK;
  }

  FILE *file = fopen(config_file, "r");
  if (!file) {
    if (errno == ENOENT) {
      chroma_log("INFO", "Config file not found: %s (using defaults)",
                 config_file);
      return CHROMA_OK;
    } else {
      chroma_log("ERROR", "Failed to open config file %s: %s", config_file,
                 strerror(errno));
      return CHROMA_ERROR_CONFIG;
    }
  }

  chroma_log("INFO", "Loading configuration from: %s", config_file);

  char line[1024];
  int line_number = 0;
  int parse_errors = 0;

  while (fgets(line, sizeof(line), file)) {
    line_number++;

    char *newline = strchr(line, '\n');
    if (newline) {
      *newline = '\0';
    }

    if (parse_config_line(config, line, line_number) != CHROMA_OK) {
      parse_errors++;
    }
  }

  fclose(file);

  if (parse_errors > 0) {
    chroma_log("WARN", "Config file contained %d errors", parse_errors);
    // Continue anyway with partial configuration
  }

  chroma_log("INFO",
             "Loaded configuration: %d output mappings, default image: %s",
             config->mapping_count, config->default_image);

  return CHROMA_OK;
}

// Free configuration resources
void chroma_config_free(chroma_config_t *config) {
  if (!config) {
    return;
  }

  memset(config->mappings, 0, sizeof(config->mappings));
  config->mapping_count = 0;

  memset(config->default_image, 0, sizeof(config->default_image));

  chroma_log("DEBUG", "Freed configuration");
}

// Get image path for specific output
const char *chroma_config_get_image_for_output(chroma_config_t *config,
                                               const char *output_name) {
  if (!config || !output_name) {
    return NULL;
  }

  // Look for specific output mapping
  for (int i = 0; i < config->mapping_count; i++) {
    if (strcmp(config->mappings[i].output_name, output_name) == 0) {
      chroma_log("DEBUG", "Found specific mapping for output %s: %s",
                 output_name, config->mappings[i].image_path);
      return config->mappings[i].image_path;
    }
  }

  // Return default image if no specific mapping found
  if (strlen(config->default_image) > 0) {
    chroma_log("DEBUG", "Using default image for output %s: %s", output_name,
               config->default_image);
    return config->default_image;
  }

  chroma_log("WARN", "No image configured for output: %s", output_name);
  return NULL;
}

// Create a sample configuration file
int chroma_config_create_sample(const char *config_file) {
  if (!config_file) {
    return CHROMA_ERROR_INIT;
  }

  FILE *file = fopen(config_file, "w");
  if (!file) {
    chroma_log("ERROR", "Failed to create sample config file %s: %s",
               config_file, strerror(errno));
    return CHROMA_ERROR_CONFIG;
  }

  fprintf(file, "# Chroma Wallpaper Daemon Configuration\n");
  fprintf(file, "# Lines starting with # are comments\n\n");

  fprintf(file, "# Default wallpaper for outputs without specific mapping\n");
  fprintf(file, "default_image = ~/.config/chroma/default.jpg\n\n");

  fprintf(file, "# Output-specific wallpapers\n");
  fprintf(file, "# Format: output.OUTPUT_NAME = /path/to/image.jpg\n");
  fprintf(file, "# You can find output names using: wlr-randr\n");
  fprintf(file, "\n");
  fprintf(file, "# Examples:\n");
  fprintf(file, "# output.DP-1 = ~/.config/chroma/monitor1.jpg\n");
  fprintf(file, "# output.DP-2 = ~/.config/chroma/monitor2.png\n");
  fprintf(file, "# output.HDMI-A-1 = ~/.config/chroma/hdmi.jpg\n");

  fclose(file);

  chroma_log("INFO", "Created sample configuration file: %s", config_file);
  return CHROMA_OK;
}

// Print current configuration for debugging
void chroma_config_print(const chroma_config_t *config) {
  if (!config) {
    return;
  }

  chroma_log("INFO", "=== Configuration ===");
  chroma_log("INFO", "Default image: %s", config->default_image);
  chroma_log("INFO", "Daemon mode: %s", config->daemon_mode ? "true" : "false");
  chroma_log("INFO", "Output mappings: %d", config->mapping_count);

  for (int i = 0; i < config->mapping_count; i++) {
    chroma_log("INFO", "  %s -> %s", config->mappings[i].output_name,
               config->mappings[i].image_path);
  }
  chroma_log("INFO", "====================");
}
