import Foundation

/// Date parsing for the contract's mixed shapes. Two ISO-8601 datetime shapes (with / without
/// fractional seconds) coexist with date-only strings in the SAME response (digest), so a single
/// JSONDecoder.dateDecodingStrategy is impossible. Dates are decoded as `String?` raw and parsed here.
///
/// Concurrency (B-1 review): uses value-type `Date.ISO8601FormatStyle` (Sendable) as static lets —
/// no shared reference formatter, so no `nonisolated(unsafe)`.
///
/// Date-only (B-1 r2 review): `digest_date` should be DISPLAYED from its raw string (no Date conversion)
/// to avoid KST off-by-one; `parse` here fixes date-only to UTC and is for sort/window math only.
public enum DSDate {
    private static let isoFractional = Date.ISO8601FormatStyle(includingFractionalSeconds: true)
    private static let isoPlain = Date.ISO8601FormatStyle(includingFractionalSeconds: false)

    public static func parse(_ s: String?) -> Date? {
        guard let s, !s.isEmpty else { return nil }
        if let d = try? isoFractional.parse(s) { return d }
        if let d = try? isoPlain.parse(s) { return d }
        return parseDateOnly(s)
    }

    /// Date-only "YYYY-MM-DD" fixed to UTC (avoids timezone off-by-one). Value-type Calendar, Sendable-safe.
    private static func parseDateOnly(_ s: String) -> Date? {
        let p = s.split(separator: "-")
        guard p.count == 3, let y = Int(p[0]), let m = Int(p[1]), let d = Int(p[2]) else { return nil }
        var comps = DateComponents()
        comps.year = y; comps.month = m; comps.day = d
        var cal = Calendar(identifier: .gregorian)
        cal.timeZone = TimeZone(identifier: "UTC")!
        return cal.date(from: comps)
    }
}
