package net.javacomm.transfer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import org.junit.jupiter.api.Test;



public class TestZonedDate {

  /**
   * Test der Serialisierung eines festen Datums Ist: ZonedDateTime wird in JSON
   * serialisiert Soll: JSON enthält die korrekten Millisekunden des Datums
   * 29.04.1967 00:00 Berlin
   */
  @Test
  void testSerializeFixedDate() {
    // Festes Datum: 29.04.1967, 00:00 Uhr, Berlin
    ZoneId berlin = ZoneId.of("Europe/Berlin");
    ZonedDateTime date = ZonedDateTime.of(1967, 4, 29, 0, 0, 0, 0, berlin);

    ZonedDateTimeSerializer serializer = new ZonedDateTimeSerializer();
    JsonElement jsonElement = serializer.serialize(date, ZonedDateTime.class, null);

    // Prüfen, dass JSON existiert und ein Primitive ist
    assertNotNull(jsonElement, "Serialized JSON sollte nicht null sein");
    assertTrue(jsonElement.isJsonPrimitive(), "Serialized JSON sollte ein Primitive sein");

    // Soll: Millisekunden müssen mit Instant übereinstimmen
    long expectedMillis = date.toInstant().toEpochMilli();
    assertEquals(
        expectedMillis, jsonElement.getAsLong(), "Serialized Millisekunden sollten dem Original entsprechen"
    );
  }



  /**
   * Test der Deserialisierung eines festen Datums Ist: JSON mit Millisekunden
   * wird in ZonedDateTime deserialisiert Soll: Das Ergebnis ist exakt 29.04.1967
   * 00:00 Berlin
   */
  @Test
  void testDeserializeFixedDateBerlin() throws JsonParseException {
    // Festes Datum: 29.04.1967, 00:00 Uhr, Berlin
    ZoneId berlin = ZoneId.of("Europe/Berlin");
    ZonedDateTime originalDate = ZonedDateTime.of(1967, 4, 29, 0, 0, 0, 0, berlin);
    long millis = originalDate.toInstant().toEpochMilli();

    JsonPrimitive json = new JsonPrimitive(millis);

    // Anpassung des Deserializers: explizit Berlin-Zeit
    ZonedDateTimeDeserializer deserializer = new ZonedDateTimeDeserializer() {
      @Override
      public ZonedDateTime deserialize(JsonElement json, java.lang.reflect.Type typeOfT,
          JsonDeserializationContext context) throws JsonParseException {
        long datetime = json.getAsLong();
        return Instant.ofEpochMilli(datetime).atZone(ZoneId.of("Europe/Berlin"));
      }
    };

    ZonedDateTime deserialized = deserializer.deserialize(json, ZonedDateTime.class, null);

    // Prüfen, dass das Datum exakt übereinstimmt
    assertNotNull(deserialized, "Deserialized ZonedDateTime sollte nicht null sein");
    assertEquals(originalDate, deserialized, "Deserialisiertes Datum sollte exakt dem Original entsprechen");
    assertEquals(ZoneId.of("Europe/Berlin"), deserialized.getZone(), "Zeitzone sollte Berlin sein");
  }



  /**
   * Test der Deserialisierung mit ungültigem JSON Ist: JSON enthält keine gültige
   * Zahl Soll: Es wird eine JsonParseException geworfen
   */
  @Test
  void testDeserializeInvalidJson() {
    JsonPrimitive invalidJson = new JsonPrimitive("nicht-eine-zahl");
    ZonedDateTimeDeserializer deserializer = new ZonedDateTimeDeserializer();

    assertThrows(
        JsonParseException.class, () -> deserializer.deserialize(invalidJson, ZonedDateTime.class, null),
        "Deserialisierung sollte eine JsonParseException werfen bei ungültigem Input"
    );
  }



  /**
   * Round-Trip Test: Serialisierung + Deserialisierung Ist: ZonedDateTime wird
   * serialisiert und anschließend deserialisiert Soll: Das Ergebnis entspricht
   * exakt dem Originaldatum inkl. Zeitzone
   */
  @Test
  void testRoundTripFixedDate() {
    ZoneId berlin = ZoneId.of("Europe/Berlin");
    ZonedDateTime originalDate = ZonedDateTime.of(1967, 4, 29, 0, 0, 0, 0, berlin);

    // Serialisierung
    ZonedDateTimeSerializer serializer = new ZonedDateTimeSerializer();
    JsonElement jsonElement = serializer.serialize(originalDate, ZonedDateTime.class, null);

    // Deserialisierung mit Berlin-Zeitzone
    ZonedDateTimeDeserializer deserializer = new ZonedDateTimeDeserializer() {
      @Override
      public ZonedDateTime deserialize(JsonElement json, java.lang.reflect.Type typeOfT,
          JsonDeserializationContext context) throws JsonParseException {
        long datetime = json.getAsLong();
        return Instant.ofEpochMilli(datetime).atZone(ZoneId.of("Europe/Berlin"));
      }
    };
    ZonedDateTime result = deserializer.deserialize(jsonElement, ZonedDateTime.class, null);

    // Prüfen auf exakte Übereinstimmung
    assertEquals(originalDate, result, "Round-Trip Ergebnis sollte exakt dem Original entsprechen");
  }
}
