import Foundation

protocol DateItem {

    var identifier: String { get }

    var startDate: Date { get }

    var hasEndDate: Bool { get }

    var potentialEndDate: Date { get }
}

extension Sequence where Element: DateItem {

    /**
     Sort the elements first by their start date (newer first),
     and then by id, if the start date is equal.
     */
    func sortedByStartDateAndId() -> [Element] {
        sorted { (lhs, rhs) -> Bool in
            if lhs.startDate == rhs.startDate {
                return lhs.identifier < rhs.identifier
            }
            return lhs.startDate > rhs.startDate
        }
    }
}

extension DateItem {

    var endDate: Date? {
        hasEndDate ? potentialEndDate : nil
    }


    func dateText(in language: ContentLanguage) -> String {
        guard let endDate else {
            return DateItemStorage.dateString(for: startDate, in: language)
        }
        let endText = DateItemStorage.dateString(for: potentialEndDate, in: language)
        let prefix = DateItemStorage.datePrefixString(from: startDate, to: endDate, in: language)
        return prefix + " - " + endText
    }
}

private enum DateItemStorage {

    static func datePrefixString(from start: Date, to end: Date, in language: ContentLanguage) -> String {
        guard Calendar.current.isDate(start, equalTo: end, toGranularity: .year) else {
            // Different year, return full string
            return DateItemStorage.dateString(for: start, in: language)
        }
        guard Calendar.current.isDate(start, equalTo: end, toGranularity: .month) else {
            // Different month
            return DateItemStorage.dayAndMonth(of: start, in: language)
        }

        return DateItemStorage.day.string(from: start)
    }


    static let englishDate: DateFormatter = {
        let df = DateFormatter()
        df.locale = .init(identifier: "en")
        df.dateFormat = "d. MMMM yyyy"
        return df
    }()

    static let germanDate: DateFormatter = {
        let df = DateFormatter()
        df.locale = .init(identifier: "de")
        df.dateFormat = "d. MMMM yyyy"
        return df
    }()

    static let englishDayAndMonth: DateFormatter = {
        let df = DateFormatter()
        df.locale = .init(identifier: "en")
        df.dateFormat = "d. MMMM"
        return df
    }()

    static let germanDayAndMonth: DateFormatter = {
        let df = DateFormatter()
        df.locale = .init(identifier: "de")
        df.dateFormat = "d. MMMM"
        return df
    }()

    static let day: DateFormatter = {
        let df = DateFormatter()
        df.dateFormat = "d."
        return df
    }()

    static func dayAndMonth(of date: Date, in language: ContentLanguage) -> String {
        switch language {
        case .english: return englishDayAndMonth.string(from: date)
        case .german: return germanDayAndMonth.string(from: date)
        }
    }

    static func dateString(for date: Date, in language: ContentLanguage) -> String {
        switch language {
        case .english: return englishDate.string(from: date)
        case .german: return germanDate.string(from: date)
        }
    }
}
