#define _DEFAULT_SOURCE /* for timegm */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "reading.h"
#include "i18n.h"

typedef enum { FORMAT_TEXT, FORMAT_HTML, FORMAT_JSON } OutputFormat;

static void print_usage(const char *prog) {
  fprintf(stderr,
    "Usage: %s --seed <string> --birth-date YYYY-MM-DD --birth-time HH:MM\n"
    "          --birth-utc-offset <hours> --birth-lat <deg> --birth-lon <deg>\n"
    "          [--date YYYY-MM-DDTHH:MM] [--format text|html|json]\n"
    "          [--lang <code>] [--i18n-dir <path>]\n\n"
    "  --seed              Tarot seed string; same seed -> same Celtic Cross spread.\n"
    "  --birth-date/time   Birth date/time in local clock time.\n"
    "  --birth-utc-offset  Hours to subtract from local birth time to get UTC (e.g. 2 for CEST).\n"
    "  --birth-lat/lon     Birth location in decimal degrees (north/east positive).\n"
    "  --date              UTC moment for today's transits/tarot draw. Defaults to now.\n"
    "  --format            Output format, defaults to text. html links card art via\n"
    "                      img/<file>, i.e. it expects to be saved next to the img/\n"
    "                      directory that `make images` copies into dist/. json uses\n"
    "                      stable, language-independent identifiers (not --lang's\n"
    "                      display text) - it's the interpreter/ module's input format.\n"
    "  --lang              Reading language code, defaults to en. Matches a file\n"
    "                      named <code>.lang in --i18n-dir (see engine/i18n/).\n"
    "  --i18n-dir          Directory to look up <lang>.lang in. Defaults to the\n"
    "                      i18n/ directory next to this binary (`make i18n`\n"
    "                      copies engine/i18n/ there as dist/i18n/).\n",
    prog);
}

/* Default --i18n-dir: <directory this binary was invoked from>/i18n, so
 * `dist/deck-engine ...` finds dist/i18n/ regardless of the caller's cwd.
 * If argv[0] has no '/' (looked up via $PATH), falls back to a relative
 * "i18n" - callers in that situation should pass --i18n-dir explicitly. */
static void default_i18n_path(const char *argv0, const char *lang, char *out, size_t out_size) {
  const char *slash = strrchr(argv0, '/');
  if (slash) {
    int dir_len = (int)(slash - argv0);
    snprintf(out, out_size, "%.*s/i18n/%s.lang", dir_len, argv0, lang);
  } else {
    snprintf(out, out_size, "i18n/%s.lang", lang);
  }
}

static int require_arg(int argc, char **argv, int *i, const char *name, const char **out) {
  if (*i + 1 >= argc) {
    fprintf(stderr, "Missing value for %s\n", name);
    return -1;
  }
  *out = argv[++*i];
  return 0;
}

int main(int argc, char **argv) {
  const char *seed = NULL;
  const char *birth_date = NULL, *birth_time = NULL;
  const char *birth_utc_offset = NULL, *birth_lat = NULL, *birth_lon = NULL;
  const char *date_str = NULL, *format_str = "text";
  const char *lang = "en", *i18n_dir_arg = NULL;

  for (int i = 1; i < argc; i++) {
    const char *arg = argv[i];
    int rc = 0;
    if (strcmp(arg, "--seed") == 0) rc = require_arg(argc, argv, &i, arg, &seed);
    else if (strcmp(arg, "--birth-date") == 0) rc = require_arg(argc, argv, &i, arg, &birth_date);
    else if (strcmp(arg, "--birth-time") == 0) rc = require_arg(argc, argv, &i, arg, &birth_time);
    else if (strcmp(arg, "--birth-utc-offset") == 0) rc = require_arg(argc, argv, &i, arg, &birth_utc_offset);
    else if (strcmp(arg, "--birth-lat") == 0) rc = require_arg(argc, argv, &i, arg, &birth_lat);
    else if (strcmp(arg, "--birth-lon") == 0) rc = require_arg(argc, argv, &i, arg, &birth_lon);
    else if (strcmp(arg, "--date") == 0) rc = require_arg(argc, argv, &i, arg, &date_str);
    else if (strcmp(arg, "--format") == 0) rc = require_arg(argc, argv, &i, arg, &format_str);
    else if (strcmp(arg, "--lang") == 0) rc = require_arg(argc, argv, &i, arg, &lang);
    else if (strcmp(arg, "--i18n-dir") == 0) rc = require_arg(argc, argv, &i, arg, &i18n_dir_arg);
    else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
      print_usage(argv[0]);
      return 0;
    } else {
      fprintf(stderr, "Unknown argument: %s\n", arg);
      print_usage(argv[0]);
      return 1;
    }
    if (rc != 0) {
      print_usage(argv[0]);
      return 1;
    }
  }

  if (!seed || !birth_date || !birth_time || !birth_utc_offset || !birth_lat || !birth_lon) {
    fprintf(stderr, "Missing required argument(s).\n\n");
    print_usage(argv[0]);
    return 1;
  }

  BirthData birth = {0};
  if (sscanf(birth_date, "%d-%d-%d", &birth.year, &birth.month, &birth.day) != 3) {
    fprintf(stderr, "Invalid --birth-date, expected YYYY-MM-DD\n");
    return 1;
  }
  if (sscanf(birth_time, "%d:%d", &birth.hour, &birth.minute) != 2) {
    fprintf(stderr, "Invalid --birth-time, expected HH:MM\n");
    return 1;
  }
  birth.utc_offset_hours = atof(birth_utc_offset);
  birth.latitude = atof(birth_lat);
  birth.longitude = atof(birth_lon);

  time_t utc_moment;
  if (date_str) {
    struct tm tm_date = {0};
    int second = 0;
    if (sscanf(date_str, "%d-%d-%dT%d:%d:%d", &tm_date.tm_year, &tm_date.tm_mon,
               &tm_date.tm_mday, &tm_date.tm_hour, &tm_date.tm_min, &second) < 5) {
      fprintf(stderr, "Invalid --date, expected YYYY-MM-DDTHH:MM[:SS]\n");
      return 1;
    }
    tm_date.tm_year -= 1900;
    tm_date.tm_mon -= 1;
    tm_date.tm_sec = second;
    utc_moment = timegm(&tm_date);
  } else {
    utc_moment = time(NULL);
  }

  OutputFormat format;
  if (strcmp(format_str, "text") == 0) format = FORMAT_TEXT;
  else if (strcmp(format_str, "html") == 0) format = FORMAT_HTML;
  else if (strcmp(format_str, "json") == 0) format = FORMAT_JSON;
  else {
    fprintf(stderr, "Invalid --format, expected text, html, or json\n");
    return 1;
  }

  char i18n_path[512];
  if (i18n_dir_arg) {
    snprintf(i18n_path, sizeof i18n_path, "%s/%s.lang", i18n_dir_arg, lang);
  } else {
    default_i18n_path(argv[0], lang, i18n_path, sizeof i18n_path);
  }
  if (!i18n_load(i18n_path) && strcmp(lang, "en") != 0) {
    fprintf(stderr, "warning: could not load translations for '%s' from %s - "
                     "falling back to built-in English\n", lang, i18n_path);
  }

  DailyReading reading;
  reading_generate(seed, &birth, utc_moment, &reading);

  if (format == FORMAT_HTML) {
    reading_print_html(&reading, stdout);
  } else if (format == FORMAT_JSON) {
    reading_print_json(&reading, stdout);
  } else {
    reading_print_text(&reading, stdout);
  }
  return 0;
}
