package jnpf.attendance.schedule;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.SignStyle;
import java.time.temporal.ChronoField;
import java.util.Optional;

/**
 * 历史排班/营业额按日 VO 中 {@code day} 字段静默归一为 ISO {@code yyyy-MM-dd}，
 * 与 {@link LocalDate#toString()} 一致以便与相似日的 {@code LocalDate} 对齐。
 *
 * <p>支持：<code>yyyy-MM-dd</code>、{@code yyyy/M/d} 变体、{@code yyyyMMdd}（8 位数字）。
 */
public final class AttendanceScheduleDayParse {

    private AttendanceScheduleDayParse() {}

    private static final DateTimeFormatter ISO = DateTimeFormatter.ISO_LOCAL_DATE;

    /** 支持 2026-1-05、2026/01/05 等非补零写法 */
    private static final DateTimeFormatter SLASH_RELAXED =
            new DateTimeFormatterBuilder()
                    .appendValue(ChronoField.YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
                    .appendLiteral('/')
                    .appendValue(ChronoField.MONTH_OF_YEAR, 1, 2, SignStyle.NOT_NEGATIVE)
                    .appendLiteral('/')
                    .appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE)
                    .toFormatter();

    private static final DateTimeFormatter COMPACT_BASIC =
            DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 若能解析日历日则返回 ISO 字符串；否则 empty（调用方仍可回退原始 key）。
     */
    public static Optional<String> normalizeToIsoDayString(String raw) {
        return tryParse(raw).map(LocalDate::toString);
    }

    /** 静默解析为 {@link LocalDate}。 */
    public static Optional<LocalDate> tryParse(String raw) {
        if (raw == null) {
            return Optional.empty();
        }
        String s = raw.trim();
        if (s.isEmpty()) {
            return Optional.empty();
        }
        Optional<LocalDate> iso = tryIso(s);
        if (iso.isPresent()) {
            return iso;
        }
        Optional<LocalDate> slash = trySlash(s);
        if (slash.isPresent()) {
            return slash;
        }
        Optional<LocalDate> compact = tryCompact(s);
        if (compact.isPresent()) {
            return compact;
        }
        return Optional.empty();
    }

    private static Optional<LocalDate> tryIso(String s) {
        try {
            return Optional.of(LocalDate.parse(s, ISO));
        } catch (DateTimeParseException e) {
            return Optional.empty();
        }
    }

    private static Optional<LocalDate> trySlash(String s) {
        try {
            if (!s.contains("/")) {
                return Optional.empty();
            }
            return Optional.of(LocalDate.parse(s, SLASH_RELAXED));
        } catch (DateTimeParseException e) {
            return Optional.empty();
        }
    }

    private static Optional<LocalDate> tryCompact(String s) {
        if (s.length() != 8 || !s.chars().allMatch(Character::isDigit)) {
            return Optional.empty();
        }
        try {
            return Optional.of(LocalDate.parse(s, COMPACT_BASIC));
        } catch (DateTimeParseException e) {
            return Optional.empty();
        }
    }
}
