import Foundation

/// Utility for formatting dates in a human-readable format
/// Mirrors the shared Kotlin DateUtils for consistent date display
enum DateUtils {

    private static let isoDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()

    private static let isoDateTimeFormatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        return formatter
    }()

    private static let isoDateTimeSimpleFormatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        return formatter
    }()

    private static let mediumDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM d, yyyy"
        return formatter
    }()

    private static let dateTimeFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMM d, yyyy 'at' h:mm a"
        return formatter
    }()

    /// Format a date string (YYYY-MM-DD) to a human-readable format
    /// Returns "Today", "Tomorrow", "Yesterday", or "Dec 15, 2024" format
    static func formatDate(_ dateString: String?) -> String {
        guard let dateString = dateString, !dateString.isEmpty else { return "" }

        // Extract date part if it includes time
        let datePart = dateString.components(separatedBy: "T").first ?? dateString

        guard let date = isoDateFormatter.date(from: datePart) else {
            return dateString
        }

        let today = Calendar.current.startOfDay(for: Date())
        let targetDate = Calendar.current.startOfDay(for: date)

        let daysDiff = Calendar.current.dateComponents([.day], from: today, to: targetDate).day ?? 0

        switch daysDiff {
        case 0:
            return "Today"
        case 1:
            return "Tomorrow"
        case -1:
            return "Yesterday"
        default:
            return mediumDateFormatter.string(from: date)
        }
    }

    /// Format a date string to medium format (e.g., "Dec 15, 2024")
    static func formatDateMedium(_ dateString: String?) -> String {
        guard let dateString = dateString, !dateString.isEmpty else { return "" }

        // Extract date part if it includes time
        let datePart = dateString.components(separatedBy: "T").first ?? dateString

        guard let date = isoDateFormatter.date(from: datePart) else {
            return dateString
        }

        return mediumDateFormatter.string(from: date)
    }

    /// Format an ISO datetime string to a human-readable date format
    /// Handles formats like "2024-01-01T00:00:00Z" or "2024-01-01T00:00:00.000Z"
    static func formatDateTime(_ dateTimeString: String?) -> String {
        guard let dateTimeString = dateTimeString, !dateTimeString.isEmpty else { return "" }

        // Try parsing with fractional seconds first
        var date = isoDateTimeFormatter.date(from: dateTimeString)

        // Fallback to simple ISO format
        if date == nil {
            date = isoDateTimeSimpleFormatter.date(from: dateTimeString)
        }

        // Fallback to just date parsing
        guard let parsedDate = date else {
            return formatDate(dateTimeString)
        }

        let today = Calendar.current.startOfDay(for: Date())
        let targetDate = Calendar.current.startOfDay(for: parsedDate)

        let daysDiff = Calendar.current.dateComponents([.day], from: today, to: targetDate).day ?? 0

        switch daysDiff {
        case 0:
            return "Today"
        case 1:
            return "Tomorrow"
        case -1:
            return "Yesterday"
        default:
            return mediumDateFormatter.string(from: parsedDate)
        }
    }

    /// Format a datetime string with time (e.g., "Dec 15, 2024 at 3:30 PM")
    static func formatDateTimeWithTime(_ dateTimeString: String?) -> String {
        guard let dateTimeString = dateTimeString, !dateTimeString.isEmpty else { return "" }

        // Try parsing with fractional seconds first
        var date = isoDateTimeFormatter.date(from: dateTimeString)

        // Fallback to simple ISO format
        if date == nil {
            date = isoDateTimeSimpleFormatter.date(from: dateTimeString)
        }

        guard let parsedDate = date else {
            return formatDate(dateTimeString)
        }

        return dateTimeFormatter.string(from: parsedDate)
    }

    /// Format a date for relative display (e.g., "2 days ago", "in 3 days")
    static func formatRelativeDate(_ dateString: String?) -> String {
        guard let dateString = dateString, !dateString.isEmpty else { return "" }

        // Extract date part if it includes time
        let datePart = dateString.components(separatedBy: "T").first ?? dateString

        guard let date = isoDateFormatter.date(from: datePart) else {
            return dateString
        }

        let today = Calendar.current.startOfDay(for: Date())
        let targetDate = Calendar.current.startOfDay(for: date)

        let daysDiff = Calendar.current.dateComponents([.day], from: today, to: targetDate).day ?? 0

        switch daysDiff {
        case 0:
            return "Today"
        case 1:
            return "Tomorrow"
        case -1:
            return "Yesterday"
        case 2...7:
            return "in \(daysDiff) days"
        case -7 ... -2:
            return "\(-daysDiff) days ago"
        default:
            return mediumDateFormatter.string(from: date)
        }
    }

    /// Check if a date string represents a date in the past
    static func isOverdue(_ dateString: String?) -> Bool {
        guard let dateString = dateString, !dateString.isEmpty else { return false }

        // Extract date part if it includes time
        let datePart = dateString.components(separatedBy: "T").first ?? dateString

        guard let date = isoDateFormatter.date(from: datePart) else {
            return false
        }

        let today = Calendar.current.startOfDay(for: Date())
        return date < today
    }

    /// Parse a date string (YYYY-MM-DD or ISO datetime) into a Date object
    static func parseDate(_ dateString: String?) -> Date? {
        guard let dateString = dateString, !dateString.isEmpty else { return nil }

        // Extract date part if it includes time
        let datePart = dateString.components(separatedBy: "T").first ?? dateString

        return isoDateFormatter.date(from: datePart)
    }

    // MARK: - Timezone Conversion Utilities

    /// Convert a local hour (0-23) to UTC hour
    /// - Parameter localHour: Hour in the device's local timezone (0-23)
    /// - Returns: Hour in UTC (0-23)
    static func localHourToUtc(_ localHour: Int) -> Int {
        let now = Date()
        let calendar = Calendar.current

        // Create a date with the given local hour
        var components = calendar.dateComponents(in: TimeZone.current, from: now)
        components.hour = localHour
        components.minute = 0
        components.second = 0

        guard let localDate = calendar.date(from: components) else {
            return localHour
        }

        // Get the hour in UTC
        var utcCalendar = Calendar.current
        utcCalendar.timeZone = TimeZone(identifier: "UTC")!
        let utcHour = utcCalendar.component(.hour, from: localDate)

        return utcHour
    }

    /// Convert a UTC hour (0-23) to local hour
    /// - Parameter utcHour: Hour in UTC (0-23)
    /// - Returns: Hour in the device's local timezone (0-23)
    static func utcHourToLocal(_ utcHour: Int) -> Int {
        let now = Date()

        // Create a calendar in UTC
        var utcCalendar = Calendar.current
        utcCalendar.timeZone = TimeZone(identifier: "UTC")!

        // Create a date with the given UTC hour
        var components = utcCalendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: now)
        components.hour = utcHour
        components.minute = 0
        components.second = 0

        guard let utcDate = utcCalendar.date(from: components) else {
            return utcHour
        }

        // Get the hour in local timezone
        let localHour = Calendar.current.component(.hour, from: utcDate)

        return localHour
    }

    /// Format an hour (0-23) to a human-readable 12-hour format
    /// - Parameter hour: Hour in 24-hour format (0-23)
    /// - Returns: Formatted string like "8:00 AM" or "2:00 PM"
    static func formatHour(_ hour: Int) -> String {
        switch hour {
        case 0:
            return "12:00 AM"
        case 1..<12:
            return "\(hour):00 AM"
        case 12:
            return "12:00 PM"
        default:
            return "\(hour - 12):00 PM"
        }
    }
}
