package net.ranides.assira.time2;

import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

@UtilityClass
public class DateTimeFormatters {

    public static final Locale LOCALE_PL = Locale.forLanguageTag("PL");

    public static final String ISO_ZONED_PATTERN           = "yyyy-MM-dd'T'HH:mm:ss.SSSX";

    public static final DateTimeFormatter ISO_ZONED        = DateTimeFormatter.ofPattern(ISO_ZONED_PATTERN).withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter COMPRESSED       = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter DATE             = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter DATETIME         = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter DATETIME_PRECISE = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter TIME             = DateTimeFormatter.ofPattern("HH:mm").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter TIME_PRECISE     = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter YEAR             = DateTimeFormatter.ofPattern("yyyy").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter DATENAME         = DateTimeFormatter.ofPattern("dd.MM EE").withZone(DateTimeUtils.ZONE_PL);
    public static final DateTimeFormatter DAY_NAME         = DateTimeFormatter.ofPattern("EEEE").withZone(DateTimeUtils.ZONE_PL);

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatUtc(@Nullable ZonedDateTime input) {
        return format(ISO_ZONED, DateTimeUtils.withZoneUtc(input));
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatIso(@Nullable ZonedDateTime input) {
        return format(ISO_ZONED, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatCompressed(@Nullable ZonedDateTime input) {
        return format(COMPRESSED, input);
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatUtcDate(@Nullable ZonedDateTime input) {
        return formatDate(DateTimeUtils.withZoneUtc(input));
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatPolishDate(@Nullable ZonedDateTime input) {
        return formatDate(DateTimeUtils.withZonePolish(input));
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatDate(@Nullable ZonedDateTime input) {
        return format(DATE, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatDateTime(@Nullable ZonedDateTime input) {
        return format(DATETIME, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatDateTimePrecise(@Nullable ZonedDateTime input) {
        return format(DATETIME_PRECISE, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatTime(@Nullable ZonedDateTime input) {
        return format(TIME, input);
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatUtcTime(@Nullable ZonedDateTime input) {
        return formatTime(DateTimeUtils.withZoneUtc(input));
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatPolishTime(@Nullable ZonedDateTime input) {
        return formatTime(DateTimeUtils.withZonePolish(input));
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatTimePrecise(@Nullable ZonedDateTime input) {
        return format(TIME_PRECISE, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatYear(@Nullable ZonedDateTime input) {
        return format(YEAR, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatDateShort(@Nullable ZonedDateTime input) {
        return format(DATENAME, input);
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatUtcDateShort(@Nullable ZonedDateTime input) {
        return format(DATENAME, LOCALE_PL, DateTimeUtils.withZoneUtc(input));
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String formatPolishDateShort(@Nullable ZonedDateTime input) {
        return format(DATENAME, LOCALE_PL, input);
    }

    @Contract("!null -> !null; null -> null")
    public static @Nullable String getDayName(@Nullable ZonedDateTime input) {
        return format(DAY_NAME, input);
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String getPolishDayName(@Nullable ZonedDateTime input) {
        return format(DAY_NAME, LOCALE_PL, input);
    }

    @ZoneExplicit
    @Contract("!null -> !null; null -> null")
    public static @Nullable String getPolishTime(@Nullable ZonedDateTime input) {
        return format(TIME, DateTimeUtils.withZonePolish(input));
    }

    @Contract("_,!null -> !null; _,null -> null")
    public static @Nullable String format(@NotNull DateTimeFormatter format, @Nullable ZonedDateTime input) {
        return input == null ? null : format.format(input);
    }

    @Contract("_,_,!null -> !null; _,_,null -> null")
    public static @Nullable String format(@NotNull DateTimeFormatter format, @NotNull Locale locale, @Nullable ZonedDateTime input) {
        return input == null ? null : format.withLocale(locale).format(input);
    }

}
