#include <dirent.h>
#include "git_list.h"

#include "../templ/duc_git_list.h"

static orb_rc_t init(orb_module_t *self) {
  duc_git_list_t *data = self->data;
  (void) data;
  git_list_param_list_t *param_list = self->param_list;
  (void) param_list;
  return ORB_OK;
}

static int _fetch_repo_meta_sort(const void *a, const void *b) {
  const git_repo_meta_t *repo_meta_a = a;
  const git_repo_meta_t *repo_meta_b = b;
  return strcmp(repo_meta_a->name, repo_meta_b->name);
}

static int _fetch_read_text_file(char *filename, char **content) {
  FILE *fd = fopen(filename, "r");
  if (fd == NULL) return 1;
  fseek(fd, 0, SEEK_END);
  size_t len = ftell(fd);
  fseek(fd, 0, SEEK_SET);
  *content = calloc(1, sizeof(char) * (len + 1));
  fread(*content, sizeof(char), len, fd);
  (*content)[len] = '\0';
  fclose(fd);
  return 0;
}

static int _fetch_read_git_file(char *repo_path, char *filename, char **content) {
  size_t file_path_len = strlen(repo_path) + strlen(filename) + 2;
  char file_path[file_path_len];
  snprintf(file_path, file_path_len, "%s/%s", repo_path, filename);
  return _fetch_read_text_file(file_path, content);
}

static bool _fetch_git_file_exists(char *repo_path, char *filename) {
  size_t file_path_len = strlen(repo_path) + strlen(filename) + 2;
  char file_path[file_path_len];
  snprintf(file_path, file_path_len, "%s/%s", repo_path, filename);
  FILE *fp = fopen(file_path, "r");
  if (!fp) return false;
  fclose(fp);
  return true;
}

static orb_rc_t fetch(orb_module_t *self) {
  duc_git_list_t *data = self->data;
  git_list_param_list_t *param_list = self->param_list;
  if (!param_list->path) return ORB_FATAL(1);
  DIR *dir = opendir(param_list->path);
  if (dir == NULL) return ORB_FATAL(2);
  struct dirent *entry;
  while ((entry = readdir(dir)) != NULL) {
    if (entry->d_type != DT_DIR) continue;
    if (strcmp(entry->d_name, ".") == 0) continue;
    if (strcmp(entry->d_name, "..") == 0) continue;
    size_t repo_path_len = strlen(param_list->path) + strlen(entry->d_name) + 2;
    char repo_path[repo_path_len];
    snprintf(repo_path, repo_path_len, "%s/%s", param_list->path, entry->d_name);
    bool is_public = _fetch_git_file_exists(repo_path, "git-daemon-export-ok");
    if (!is_public) continue;
    data->repo_meta_list = realloc(data->repo_meta_list, sizeof(git_repo_meta_t) * (data->repo_meta_list_len + 1));
    git_repo_meta_t *repo_meta = &data->repo_meta_list[data->repo_meta_list_len];
    memset(repo_meta, 0, sizeof(git_repo_meta_t));
    repo_meta->name = strdup(entry->d_name);
    _fetch_read_git_file(repo_path, "description", &repo_meta->description);
    _fetch_read_git_file(repo_path, "owner", &repo_meta->owner);
    repo_meta->is_public = is_public;
    repo_meta->is_archive = _fetch_git_file_exists(repo_path, ".archive");
    repo_meta->is_fork = _fetch_git_file_exists(repo_path, ".fork");
    data->repo_meta_list_len++;
  }
  closedir(dir);
  qsort(data->repo_meta_list, data->repo_meta_list_len, sizeof(git_repo_meta_t), _fetch_repo_meta_sort);
  return ORB_OK;
}

static orb_rc_t render(orb_module_t *self, void *out) {
  return ORB_ERR(duc_git_list_render(out, (duc_git_list_t *) self->data));
}

static void cleanup(orb_module_t *self) {
  duc_git_list_t *data = self->data;
  if (data->repo_meta_list && data->repo_meta_list_len > 0) {
    for (size_t i = 0; i < data->repo_meta_list_len; i++) {
      git_repo_meta_t *repo_meta = &data->repo_meta_list[i];
      free(repo_meta->name);
      if (repo_meta->description) free(repo_meta->description);
      if (repo_meta->owner) free(repo_meta->owner);
    }
    free(data->repo_meta_list);
    data->repo_meta_list = NULL;
    data->repo_meta_list_len = 0;
  }
}

const orb_module_type_t GIT_LIST_TYPE = {
  .size = sizeof(duc_git_list_t),
  .init = init,
  .fetch = fetch,
  .render = render,
  .cleanup = cleanup,
};
