/**
 * Configuration handling implementation for wssshd
 *
 * Copyright (C) 2024 Stefy Lanza <stefy@nexlab.net> and SexHack.me
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include "config.h"

// Default configuration values
#define DEFAULT_PORT 9898
#define DEFAULT_WEB_PROXY_PORT 9090
#define DEFAULT_CONFIG_FILE "/etc/wssshd.conf"

static void set_default_config(wssshd_config_t *config) {
    config->config_file = NULL;
    config->host = NULL;
    config->port = DEFAULT_PORT;
    config->domain = NULL;
    config->password = NULL;
    config->web_host = NULL;
    config->web_port = 0;
    config->web_https = false;
    config->web_proxy_enabled = false;
    config->web_proxy_port = 0;
    config->websocket_tls_only = true;  // Default to TLS-only for security
    config->debug = false;
    config->debug_web = false;
    config->debug_database = false;
    config->debug_vnc = false;
    config->debug_rdp = false;
}

static void load_config_file(wssshd_config_t *config, const char *config_file) {
    FILE *fp = fopen(config_file, "r");
    if (!fp) {
        if (config->debug) {
            fprintf(stderr, "Warning: Could not open config file %s\n", config_file);
        }
        return;
    }

    // Parse config file

    char line[1024];
    char section[256] = "";

    while (fgets(line, sizeof(line), fp)) {
        // Remove trailing whitespace
        char *end = line + strlen(line) - 1;
        while (end >= line && (*end == '\n' || *end == '\r' || *end == ' ' || *end == '\t')) {
            *end-- = '\0';
        }

        // Skip empty lines and comments
        if (line[0] == '\0' || line[0] == '#' || line[0] == ';') {
            continue;
        }

        // Section header
        if (line[0] == '[' && line[strlen(line) - 1] == ']') {
            size_t section_len = strlen(line) - 2; // Remove brackets
            if (section_len < sizeof(section)) {
                memcpy(section, line + 1, section_len);
                section[section_len] = '\0';
            }
            continue;
        }

        // Key-value pair
        if (strchr(line, '=') && strcmp(section, "wssshd") == 0) {
            char *key = line;
            char *value = strchr(line, '=');
            if (value) {
                *value++ = '\0';

                // Trim whitespace
                while (*key == ' ' || *key == '\t') key++;
                end = key + strlen(key) - 1;
                while (end >= key && (*end == ' ' || *end == '\t')) *end-- = '\0';

                while (*value == ' ' || *value == '\t') value++;
                end = value + strlen(value) - 1;
                while (end >= value && (*end == ' ' || *end == '\t')) *end-- = '\0';

                // Set config values
                if (strcmp(key, "host") == 0 && !config->host) {
                    config->host = strdup(value);
                } else if (strcmp(key, "port") == 0) {
                    config->port = atoi(value);
                } else if (strcmp(key, "domain") == 0 && !config->domain) {
                    config->domain = strdup(value);
                } else if (strcmp(key, "password") == 0 && !config->password) {
                    config->password = strdup(value);
                } else if (strcmp(key, "web-host") == 0 && !config->web_host) {
                    config->web_host = strdup(value);
                } else if (strcmp(key, "web-port") == 0) {
                    config->web_port = atoi(value);
                } else if (strcmp(key, "web-https") == 0) {
                    config->web_https = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
                } else if (strcmp(key, "web-proxy") == 0) {
                    config->web_proxy_enabled = true;
                    config->web_proxy_port = atoi(value);
                } else if (strcmp(key, "web-proxy-port") == 0) {
                    config->web_proxy_port = atoi(value);
                } else if (strcmp(key, "websocket-tls-only") == 0) {
                    config->websocket_tls_only = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
                }
            }
        }
    }

    fclose(fp);
}

wssshd_config_t *load_config(int argc, char *argv[]) {
    wssshd_config_t *config = malloc(sizeof(wssshd_config_t));
    if (!config) {
        perror("Failed to allocate config");
        return NULL;
    }

    set_default_config(config);

    // Load default config file if it exists
    char *config_file = DEFAULT_CONFIG_FILE;
    if (access(config_file, F_OK) == 0) {
        config->config_file = strdup(config_file);
        if (config->debug) {
            printf("[DEBUG] Loading default config file: %s\n", config_file);
        }
        load_config_file(config, config_file);
        if (config->debug) {
            printf("[DEBUG] After loading default config: host=%s, port=%d\n", config->host, config->port);
        }
    }

    // Parse command line arguments
    static struct option long_options[] = {
        {"config", required_argument, 0, 'c'},
        {"host", required_argument, 0, 'h'},
        {"port", required_argument, 0, 'p'},
        {"domain", required_argument, 0, 'd'},
        {"password", required_argument, 0, 'P'},
        {"web-host", required_argument, 0, 'w'},
        {"web-port", required_argument, 0, 'W'},
        {"web-https", no_argument, 0, 's'},
        {"web-proxy", optional_argument, 0, 'x'},
        {"websocket-tls-only", no_argument, 0, 't'},
        {"debug", no_argument, 0, 'D'},
        {"debug-web", no_argument, 0, 'E'},
        {"debug-database", no_argument, 0, 'F'},
        {"debug-vnc", no_argument, 0, 'G'},
        {"debug-rdp", no_argument, 0, 'H'},
        {"help", no_argument, 0, '?'},
        {0, 0, 0, 0}
    };

    int opt;
    int option_index = 0;

    while ((opt = getopt_long(argc, argv, "c:h:p:d:P:w:W:stx::DEFGH?", long_options, &option_index)) != -1) {
        switch (opt) {
            case 'c':
                if (config->config_file) free(config->config_file);
                config->config_file = strdup(optarg);
                break;
            case 'h':
                if (config->host) free(config->host);
                config->host = strdup(optarg);
                break;
            case 'p':
                config->port = atoi(optarg);
                break;
            case 'd':
                if (config->domain) free(config->domain);
                config->domain = strdup(optarg);
                break;
            case 'P':
                if (config->password) free(config->password);
                config->password = strdup(optarg);
                break;
            case 'w':
                if (config->web_host) free(config->web_host);
                config->web_host = strdup(optarg);
                break;
            case 'W':
                config->web_port = atoi(optarg);
                break;
            case 's':
                config->web_https = true;
                break;
            case 'x':
                config->web_proxy_enabled = true;
                if (optarg) {
                    config->web_proxy_port = atoi(optarg);
                } else {
                    config->web_proxy_port = DEFAULT_WEB_PROXY_PORT;
                }
                break;
            case 't':
                config->websocket_tls_only = false;
                break;
            case 'D':
                config->debug = true;
                break;
            case 'E':
                config->debug_web = true;
                break;
            case 'F':
                config->debug_database = true;
                break;
            case 'G':
                config->debug_vnc = true;
                break;
            case 'H':
                config->debug_rdp = true;
                break;
            case '?':
                printf("Usage: %s [OPTIONS]\n", argv[0]);
                printf("Options:\n");
                printf("  --config FILE    Configuration file (default: %s)\n", DEFAULT_CONFIG_FILE);
                printf("  --host HOST      WebSocket server host\n");
                printf("  --port PORT      WebSocket server port (default: %d)\n", DEFAULT_PORT);
                printf("  --domain DOMAIN  Base domain name\n");
                printf("  --password PASS  Registration password\n");
                printf("  --web-host HOST  Web interface host\n");
                printf("  --web-port PORT  Web interface port\n");
                printf("  --web-https      Enable HTTPS for web interface\n");
                printf("  --web-proxy [PORT]  Enable web proxy on port (default: %d)\n", DEFAULT_WEB_PROXY_PORT);
                printf("  --websocket-tls-only  Allow non-TLS WebSocket connections (default: TLS-only)\n");
                printf("  --debug          Enable debug output\n");
                printf("  --debug-web      Enable comprehensive web interface debug output\n");
                printf("  --debug-database Enable database debug output\n");
                printf("  --debug-vnc      Enable VNC debug output\n");
                printf("  --debug-rdp      Enable RDP debug output\n");
                printf("  --help           Show this help\n");
                free_config(config);
                exit(0);
            default:
                free_config(config);
                exit(1);
        }
    }

    // Reload config file if --config was specified and it's different from default
    if (config->config_file && strcmp(config->config_file, DEFAULT_CONFIG_FILE) != 0) {
        if (config->debug) {
            printf("[DEBUG] Reloading config file: %s\n", config->config_file);
        }
        // Reset config values that could be overridden by config file
        if (config->host) { free(config->host); config->host = NULL; }
        if (config->domain) { free(config->domain); config->domain = NULL; }
        if (config->password) { free(config->password); config->password = NULL; }
        if (config->web_host) { free(config->web_host); config->web_host = NULL; }
        config->port = DEFAULT_PORT;
        config->web_port = 0;
        config->web_https = false;
        config->web_proxy_enabled = false;
        config->web_proxy_port = 0;
        config->websocket_tls_only = true;

        load_config_file(config, config->config_file);
        if (config->debug) {
            printf("[DEBUG] After reloading config: host=%s, port=%d\n", config->host, config->port);
        }
    }

    // Validate required parameters
    if (!config->host) {
        fprintf(stderr, "Error: --host is required\n");
        free_config(config);
        exit(1);
    }
    if (!config->domain) {
        fprintf(stderr, "Error: --domain is required\n");
        free_config(config);
        exit(1);
    }
    if (!config->password) {
        fprintf(stderr, "Error: --password is required\n");
        free_config(config);
        exit(1);
    }

    return config;
}

void free_config(wssshd_config_t *config) {
    if (!config) return;

    if (config->config_file) free(config->config_file);
    if (config->host) free(config->host);
    if (config->domain) free(config->domain);
    if (config->password) free(config->password);
    if (config->web_host) free(config->web_host);

    free(config);
}

void print_config(const wssshd_config_t *config) {
    printf("Configuration:\n");
    printf("  Config file: %s\n", config->config_file ? config->config_file : "none");
    printf("  Host: %s\n", config->host);
    printf("  Port: %d\n", config->port);
    printf("  Domain: %s\n", config->domain);
    printf("  Password: %s\n", config->password ? "***" : "none");
    printf("  Web host: %s\n", config->web_host ? config->web_host : "none");
    printf("  Web port: %d\n", config->web_port);
    printf("  Web HTTPS: %s\n", config->web_https ? "yes" : "no");
    printf("  Web Proxy: %s\n", config->web_proxy_enabled ? "yes" : "no");
    printf("  Web Proxy Port: %d\n", config->web_proxy_port);
    printf("  WebSocket TLS-only: %s\n", config->websocket_tls_only ? "yes" : "no");
    printf("  Debug: %s\n", config->debug ? "yes" : "no");
    printf("  Debug Web: %s\n", config->debug_web ? "yes" : "no");
    printf("  Debug Database: %s\n", config->debug_database ? "yes" : "no");
    printf("  Debug VNC: %s\n", config->debug_vnc ? "yes" : "no");
    printf("  Debug RDP: %s\n", config->debug_rdp ? "yes" : "no");
}