#define _POSIX_C_SOURCE 200809L /* for mkstemp/fdopen */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "../../engine/src/i18n.h"
#include "../src/narrative.h"

static const char *slurp(FILE *f) {
  static char buf[4096];
  long len = ftell(f);
  rewind(f);
  size_t n = fread(buf, 1, (size_t)len, f);
  buf[n] = '\0';
  return buf;
}

static void test_hard_aspect_shows_base_but_not_harmonious_bonus(void) {
  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_MOON,
               .type = ASPECT_SQUARE, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0);
  const char *text = slurp(f);

  assert(strstr(text, "depression") != NULL);
  assert(strstr(text, "past associations") == NULL);

  fclose(f);
  printf("PASS test_hard_aspect_shows_base_but_not_harmonious_bonus\n");
}

static void test_soft_aspect_adds_harmonious_bonus(void) {
  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_SUN,
               .type = ASPECT_TRINE, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0);
  const char *text = slurp(f);

  assert(strstr(text, "depression") != NULL);
  assert(strstr(text, "past associations") != NULL);

  fclose(f);
  printf("PASS test_soft_aspect_adds_harmonious_bonus\n");
}

static void test_conjunction_is_neutral_prints_base_only(void) {
  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_SUN,
               .type = ASPECT_CONJUNCTION, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0);
  const char *text = slurp(f);

  assert(strstr(text, "depression") != NULL);
  assert(strstr(text, "past associations") == NULL);

  fclose(f);
  printf("PASS test_conjunction_is_neutral_prints_base_only\n");
}

/* The Sun has no unconditional base text (see narrative.c) - a neutral
 * conjunction to it should print nothing at all. */
static void test_empty_base_and_neutral_aspect_prints_nothing(void) {
  Aspect a = { .transiting_planet = PLANET_SUN, .natal_planet = PLANET_MOON,
               .type = ASPECT_CONJUNCTION, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0);
  long len = ftell(f);

  assert(len == 0);

  fclose(f);
  printf("PASS test_empty_base_and_neutral_aspect_prints_nothing\n");
}

static void test_discordant_aspect_on_empty_base_planet(void) {
  Aspect a = { .transiting_planet = PLANET_SUN, .natal_planet = PLANET_MOON,
               .type = ASPECT_OPPOSITION, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0);
  const char *text = slurp(f);

  assert(strstr(text, "Degradation") != NULL);

  fclose(f);
  printf("PASS test_discordant_aspect_on_empty_base_planet\n");
}

static void test_known_house_adds_area_framing(void) {
  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_MOON,
               .type = ASPECT_SQUARE, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 3);
  const char *text = slurp(f);

  assert(strstr(text, "house 3") != NULL);
  assert(strstr(text, "communication") != NULL);
  assert(strstr(text, "depression") != NULL);

  fclose(f);
  printf("PASS test_known_house_adds_area_framing\n");
}

static void test_unknown_house_omits_area_framing(void) {
  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_MOON,
               .type = ASPECT_SQUARE, .orb = 1.0 };
  FILE *f = tmpfile();
  narrative_print(f, "  ", &a, 0); /* 0 = "no data" sentinel, not a real house */
  const char *text = slurp(f);

  assert(strstr(text, "house") == NULL);
  assert(strstr(text, "depression") != NULL);

  fclose(f);
  printf("PASS test_unknown_house_omits_area_framing\n");
}

/* Proves narrative_print() actually routes through i18n_get() rather
 * than just printing the hardcoded fallback - loads a translation file
 * that overrides one planet's base text and one house's area text, and
 * checks the override wins. Run last: i18n_load() replaces the
 * process-global catalog for the rest of the test binary's lifetime. */
static void test_translation_catalog_overrides_fallback_text(void) {
  char path[] = "/tmp/deck_narrative_test_XXXXXX";
  int fd = mkstemp(path);
  assert(fd >= 0);
  FILE *f = fdopen(fd, "w");
  fprintf(f, "narrative.saturn.base=UEBERSETZTER SATURN TEXT\n");
  fprintf(f, "narrative.house.3=UEBERSETZTES HAUS DREI\n");
  fclose(f);

  assert(i18n_load(path));
  unlink(path);

  Aspect a = { .transiting_planet = PLANET_SATURN, .natal_planet = PLANET_MOON,
               .type = ASPECT_CONJUNCTION, .orb = 1.0 };
  FILE *out = tmpfile();
  narrative_print(out, "", &a, 3);
  const char *text = slurp(out);

  assert(strstr(text, "UEBERSETZTER SATURN TEXT") != NULL);
  assert(strstr(text, "UEBERSETZTES HAUS DREI") != NULL);
  assert(strstr(text, "depression") == NULL); /* English fallback must not leak through */

  fclose(out);
  printf("PASS test_translation_catalog_overrides_fallback_text\n");
}

int main(void) {
  test_hard_aspect_shows_base_but_not_harmonious_bonus();
  test_soft_aspect_adds_harmonious_bonus();
  test_conjunction_is_neutral_prints_base_only();
  test_empty_base_and_neutral_aspect_prints_nothing();
  test_discordant_aspect_on_empty_base_planet();
  test_known_house_adds_area_framing();
  test_unknown_house_omits_area_framing();
  test_translation_catalog_overrides_fallback_text();
  printf("All narrative tests passed.\n");
  return 0;
}
