#include <arpa/inet.h>
#include <asm-generic/errno-base.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#define BUFFER_SIZE 4096
#define CONFIG_LINE_MAX 256
#define LOG_LINE_MAX 512
#define DEFAULT_PORT 8080
#define DEFAULT_ROOT "./webroot"

// 服务器配置结构
typedef struct {
  char root[256];
  int port;
} ServerConfig;

// 全局变量
ServerConfig config;
FILE *log_file = NULL;

// 函数声明
void load_config(const char *config_file);
void init_log();
void log_request(const char *client_ip, const char *method, const char *path);
void handle_client(int client_socket, struct sockaddr_in client_addr);
void send_response(int client_socket, const char *status,
                   const char *content_type, const char *body, int body_length);
void send_file(int client_socket, const char *file_path);
void send_404(int client_socket);
char *get_mime_type(const char *file_path);
void signal_handler(int sig);

int main() {
  int server_socket, client_socket;
  struct sockaddr_in server_addr, client_addr;
  socklen_t client_len = sizeof(client_addr);

  // 信号处理
  signal(SIGINT, signal_handler);
  signal(SIGTERM, signal_handler);

  // 加载配置文件
  load_config("webserver.ini");

  // 初始化日志
  init_log();
  printf("Starting Web Server...\n");
  printf("Root directory: %s\n", config.root);
  printf("Port: %d\n", config.port);

  // 创建socket
  server_socket = socket(AF_INET, SOCK_STREAM, 0);
  if (server_socket < 0) {
    perror("Socket creation failed");
    exit(1);
  }

  // 设置socket选项以重用地址
  int opt = 1;
  if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) <
      0) {
    perror("setsockopt failed");
    exit(1);
  }

  // 配置服务器地址
  memset(&server_addr, 0, sizeof(server_addr));
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = INADDR_ANY;
  server_addr.sin_port = htons(config.port);

  // 绑定socket
  if (bind(server_socket, (struct sockaddr *)&server_addr,
           sizeof(server_addr)) < 0) {
    perror("Bind failed");
    close(server_socket);
    exit(1);
  }

  // 监听连接
  if (listen(server_socket, 10) < 0) {
    perror("Listen failed");
    close(server_socket);
    exit(1);
  }

  printf("Server is listening on port %d...\n", config.port);
  printf("Press Ctrl+C to stop the server.\n\n");

  // 主服务循环
  while (1) {
    client_socket =
        accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
    if (client_socket < 0) {
      if (errno == EINTR) {
        continue;
      }
      perror("Accept failed");
      continue;
    }

    // 处理客户端请求
    handle_client(client_socket, client_addr);
    close(client_socket);
  }

  close(server_socket);
  if (log_file) {
    fclose(log_file);
  }
  return EXIT_SUCCESS;
}

// 加载配置文件
void load_config(const char *config_file) {
  FILE *file;
  char line[CONFIG_LINE_MAX];
  char key[128], value[128];

  // 设置默认值
  strcpy(config.root, DEFAULT_ROOT);
  config.port = DEFAULT_PORT;

  file = fopen(config_file, "r");
  if (!file) {
    printf("Warning: Cannot open config file '%s', using defaults.\n",
           config_file);
    return;
  }

  while (fgets(line, sizeof(line), file)) {
    // 跳过注释和空行
    if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') {
      continue;
    }

    // 解析键值对
    if (sscanf(line, "%127[^=]=%127s", key, value) == 2) {
      // 去除键的空格
      char *k = key;
      while (*k == ' ' || *k == '\t')
        k++;
      char *k_end = k + strlen(k) - 1;
      while (k_end > k && (*k_end == ' ' || *k_end == '\t' || *k_end == '\n' ||
                           *k_end == '\r')) {
        *k_end = '\0';
        k_end--;
      }

      if (strcmp(k, "root") == 0) {
        strcpy(config.root, value);
      } else if (strcmp(k, "port") == 0) {
        config.port = atoi(value);
        if (config.port <= 0 || config.port > 65535) {
          config.port = DEFAULT_PORT;
        }
      }
    }
  }

  fclose(file);
}

// 初始化日志
void init_log() {
  log_file = fopen("webserver.log", "a");
  if (!log_file) {
    printf("Warning: Cannot open log file, logging to stdout only.\n");
  }
}

// 记录访问日志
void log_request(const char *client_ip, const char *method, const char *path) {
  time_t now;
  struct tm *tm_info;
  char time_str[64];
  char log_line[LOG_LINE_MAX];

  time(&now);
  tm_info = localtime(&now);
  strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", tm_info);

  snprintf(log_line, sizeof(log_line), "%s IP:%s %s %s\n", time_str, client_ip,
           method, path);

  // 输出到控制台
  printf("%s", log_line);

  // 输出到日志文件
  if (log_file) {
    fprintf(log_file, "%s", log_line);
    fflush(log_file);
  }
}

// 处理客户端请求
void handle_client(int client_socket, struct sockaddr_in client_addr) {
  char buffer[BUFFER_SIZE];
  char method[16], path[256], version[16];
  char *client_ip = inet_ntoa(client_addr.sin_addr);

  // 接收HTTP请求
  int bytes_received = recv(client_socket, buffer, sizeof(buffer) - 1, 0);
  if (bytes_received <= 0) {
    return;
  }

  buffer[bytes_received] = '\0';

  // 解析HTTP请求行
  if (sscanf(buffer, "%15s %255s %15s", method, path, version) != 3) {
    send_404(client_socket);
    return;
  }

  // 记录访问日志
  log_request(client_ip, method, path);

  // 只处理GET请求
  if (strcmp(method, "GET") != 0) {
    send_response(client_socket, "405 Method Not Allowed", "text/html",
                  "<h1>405 Method Not Allowed</h1>", 30);
    return;
  }

  // 构建完整文件路径
  char file_path[512];
  if (strcmp(path, "/") == 0) {
    snprintf(file_path, sizeof(file_path), "%s/index.html", config.root);
  } else {
    snprintf(file_path, sizeof(file_path), "%s%s", config.root, path);
  }

  // 检查文件是否存在
  struct stat file_stat;
  if (stat(file_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
    send_file(client_socket, file_path);
  } else {
    send_404(client_socket);
  }
}

// 发送HTTP响应
void send_response(int client_socket, const char *status,
                   const char *content_type, const char *body,
                   int body_length) {
  char header[1024];

  snprintf(header, sizeof(header),
           "HTTP/1.1 %s\r\n"
           "Content-Type: %s\r\n"
           "Content-Length: %d\r\n"
           "Connection: close\r\n"
           "\r\n",
           status, content_type, body_length);

  send(client_socket, header, strlen(header), 0);
  if (body && body_length > 0) {
    send(client_socket, body, body_length, 0);
  }
}

// 发送文件
void send_file(int client_socket, const char *file_path) {
  FILE *file = fopen(file_path, "rb");
  if (!file) {
    send_404(client_socket);
    return;
  }

  // 获取文件大小
  fseek(file, 0, SEEK_END);
  long file_size = ftell(file);
  fseek(file, 0, SEEK_SET);

  // 获取MIME类型
  char *mime_type = get_mime_type(file_path);

  // 发送HTTP头
  char header[1024];
  snprintf(header, sizeof(header),
           "HTTP/1.1 200 OK\r\n"
           "Content-Type: %s\r\n"
           "Content-Length: %ld\r\n"
           "Connection: close\r\n"
           "\r\n",
           mime_type, file_size);

  send(client_socket, header, strlen(header), 0);

  // 发送文件内容
  char file_buffer[4096];
  size_t bytes_read;
  while ((bytes_read = fread(file_buffer, 1, sizeof(file_buffer), file)) > 0) {
    send(client_socket, file_buffer, bytes_read, 0);
  }

  fclose(file);
}

// 发送404错误
void send_404(int client_socket) {
  const char *body = "<html><body><h1>404 Not Found</h1><p>The requested page "
                     "was not found.</p></body></html>";
  send_response(client_socket, "404 Not Found", "text/html", body,
                strlen(body));
}

// 获取MIME类型
char *get_mime_type(const char *file_path) {
  const char *ext = strrchr(file_path, '.');
  if (!ext)
    return "application/octet-stream";

  if (strcmp(ext, ".html") == 0 || strcmp(ext, ".htm") == 0) {
    return "text/html";
  } else if (strcmp(ext, ".css") == 0) {
    return "text/css";
  } else if (strcmp(ext, ".js") == 0) {
    return "application/javascript";
  } else if (strcmp(ext, ".jpg") == 0 || strcmp(ext, ".jpeg") == 0) {
    return "image/jpeg";
  } else if (strcmp(ext, ".png") == 0) {
    return "image/png";
  } else if (strcmp(ext, ".gif") == 0) {
    return "image/gif";
  } else if (strcmp(ext, ".txt") == 0) {
    return "text/plain";
  }

  return "application/octet-stream";
}

// 信号处理函数
void signal_handler(int sig) {
  char *signal_name = (sig == SIGINT) ? "SIGINT" : "SIGTERM";
  time_t now;
  struct tm *tm_info;
  char time_str[64];

  // 获取当前时间
  time(&now);
  tm_info = localtime(&now);
  strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", tm_info);

  // 记录关闭信息
  printf("\n[%s] Received %s signal. Shutting down server...\n", time_str,
         signal_name);

  // 如果日志文件打开，记录关闭信息并关闭
  if (log_file) {
    fprintf(log_file, "[%s] Server shutdown initiated by %s\n", time_str,
            signal_name);
    fflush(log_file);
    fclose(log_file);
    log_file = NULL;
  }

  // 等待1秒以确保所有pending的写操作完成
  sleep(1);

  printf("Server shutdown complete.\n");
  exit(0);
}
