package dev.photosync.core.persistence;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import dev.photosync.core.provider.ProviderId;

import java.io.IOException;
import java.time.Instant;
import java.time.format.DateTimeParseException;

/**
 * The handful of value types PhotoSync persists that Gson would otherwise write
 * as nested objects.
 *
 * <p>Everything else -- records, enums, primitives -- Gson already handles, and
 * on purpose: the config and queue models are built out of types the library
 * understands so that this class stays this short. Note the Gson version we
 * compile against (2.10, the oldest Minecraft ships in the supported range) is
 * also the first that can construct records, which is what makes that possible.
 */
final class Adapters {

    private Adapters() {
    }

    static Gson newGson() {
        return new GsonBuilder()
                .registerTypeAdapter(ProviderId.class, new ProviderIdAdapter().nullSafe())
                .registerTypeAdapter(Instant.class, new InstantAdapter().nullSafe())
                .setPrettyPrinting()
                .disableHtmlEscaping()
                .create();
    }

    /** Writes {@code "immich"} rather than {@code {"key":"immich"}}. */
    private static final class ProviderIdAdapter extends TypeAdapter<ProviderId> {

        @Override
        public void write(JsonWriter out, ProviderId value) throws IOException {
            out.value(value.key());
        }

        @Override
        public ProviderId read(JsonReader in) throws IOException {
            String key = in.nextString();
            return key.isBlank() ? null : new ProviderId(key);
        }
    }

    /** ISO-8601, so the queue file stays readable when someone goes looking. */
    private static final class InstantAdapter extends TypeAdapter<Instant> {

        @Override
        public void write(JsonWriter out, Instant value) throws IOException {
            out.value(value.toString());
        }

        @Override
        public Instant read(JsonReader in) throws IOException {
            if (in.peek() == JsonToken.NUMBER) {
                return Instant.ofEpochMilli(in.nextLong());
            }
            try {
                return Instant.parse(in.nextString());
            } catch (DateTimeParseException e) {
                return null;
            }
        }
    }
}
