package org.raddatz.familienarchiv.document;

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

/**
 * Produces the honest German date label baked into an import title — at exactly
 * the precision the data claims, never finer. This is the Java half of the
 * single source of truth shared with the frontend {@code formatDocumentDate}
 * (TypeScript): both are asserted against {@code docs/date-label-fixtures.json}
 * so the two implementations cannot drift (see #666).
 *
 * <p>Import titles are always German, so the labels here are the German
 * canonical form (mirroring the {@code de} Paraglide messages used by the UI).
 */
final class DocumentTitleFormatter {

    private static final DateTimeFormatter LONG = DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.GERMAN);
    private static final DateTimeFormatter MONTH_YEAR = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.GERMAN);
    private static final DateTimeFormatter MEDIUM = DateTimeFormatter.ofPattern("d. MMM yyyy", Locale.GERMAN);
    private static final DateTimeFormatter DAY_MONTH = DateTimeFormatter.ofPattern("d. MMM", Locale.GERMAN);

    private static final String UNKNOWN = "Datum unbekannt";
    private static final String APPROX_PREFIX = "ca.";
    private static final String OPEN_RANGE_PREFIX = "ab";

    private DocumentTitleFormatter() {
    }

    /**
     * @param date the sort/filter anchor day; null for UNKNOWN rows
     * @param precision descriptive precision metadata
     * @param end the RANGE end day; null means an open-ended range
     * @param raw the verbatim spreadsheet cell, used only to pick a season word
     * @return the honest German label
     */
    static String formatTitleDate(LocalDate date, DatePrecision precision, LocalDate end, String raw) {
        if (precision == DatePrecision.UNKNOWN || date == null) {
            return UNKNOWN;
        }
        return switch (precision) {
            case DAY -> LONG.format(date);
            case MONTH -> MONTH_YEAR.format(date);
            case SEASON -> seasonLabel(date, raw);
            case YEAR -> String.valueOf(date.getYear());
            case APPROX -> APPROX_PREFIX + " " + date.getYear();
            case RANGE -> rangeLabel(date, end);
            case UNKNOWN -> UNKNOWN;
        };
    }

    private static String seasonLabel(LocalDate date, String raw) {
        Season season = seasonFromRaw(raw);
        if (season == null) {
            season = seasonOfMonth(date.getMonthValue());
        }
        return season.german + " " + date.getYear();
    }

    private static String rangeLabel(LocalDate start, LocalDate end) {
        if (end == null) {
            return OPEN_RANGE_PREFIX + " " + MEDIUM.format(start);
        }
        if (end.equals(start)) {
            return MEDIUM.format(start);
        }
        if (start.getYear() != end.getYear()) {
            return MEDIUM.format(start) + " – " + MEDIUM.format(end);
        }
        if (start.getMonthValue() == end.getMonthValue()) {
            return start.getDayOfMonth() + ".–" + MEDIUM.format(end);
        }
        return DAY_MONTH.format(start) + " – " + MEDIUM.format(end);
    }

    // ─── season mapping — mirrors the normalizer's representative months ─────────────

    private enum Season {
        SPRING("Frühling"),
        SUMMER("Sommer"),
        AUTUMN("Herbst"),
        WINTER("Winter");

        private final String german;

        Season(String german) {
            this.german = german;
        }
    }

    private static Season seasonOfMonth(int month) {
        if (month >= 3 && month <= 5) return Season.SPRING;
        if (month >= 6 && month <= 8) return Season.SUMMER;
        if (month >= 9 && month <= 11) return Season.AUTUMN;
        return Season.WINTER;
    }

    private static Season seasonFromRaw(String raw) {
        if (raw == null || raw.isBlank()) return null;
        String token = raw.trim().split("\\s+")[0].toLowerCase(Locale.GERMAN);
        return switch (token) {
            case "frühling", "frühjahr" -> Season.SPRING;
            case "sommer" -> Season.SUMMER;
            case "herbst" -> Season.AUTUMN;
            case "winter" -> Season.WINTER;
            default -> null;
        };
    }
}
