import ActivityKit
import Foundation

struct TimetableActivityAttributes: ActivityAttributes {
    public struct ContentState: Codable, Hashable {
        var isBreak: Bool
        var lessonName: String
        var lessonTheme: String?
        var roomName: String?
        var teacherName: String?
        var startTime: Date
        var endTime: Date
        var lessonNumber: Int?

        var mode: String? // "lesson" | "break" | "seasonalBreak" | "xmas" | "newYearEve" | "newYearDay"
        var message: String?
        var season: String?

        var nextLessonName: String?
        var nextRoomName: String?
        var nextStartTime: Date?

        var isSubstitution: Bool
        var isCancelled: Bool
        var substituteTeacher: String?

        var currentTime: Date

        var labels: Labels?
        var tokenExpirationWarning: String?
        var tokenExpired: Bool?
        var compactTimerText: String?
        var lessonIcon: String?

        struct Labels: Codable, Hashable {
            var title: String?
            var timerLabel: String?
            var cancelledText: String?
            var substitutionText: String?
            var roomLabel: String?
            var teacherLabel: String?
            var themeLabel: String?
            var nextLabel: String?
            var firstLessonLabel: String?
            var startTimeLabel: String?
            var lessonNumberLabel: String?
            var nextRoomLabel: String?
            var remainingLabel: String?
        }

        enum CodingKeys: String, CodingKey {
            case isBreak
            case lessonName
            case lessonTheme
            case roomName
            case teacherName
            case startTime
            case endTime
            case lessonNumber
            case mode
            case message
            case season
            case nextLessonName
            case nextRoomName
            case nextStartTime
            case isSubstitution
            case isCancelled
            case substituteTeacher
            case currentTime
            case labels
            case tokenExpirationWarning
            case tokenExpired
            case compactTimerText
            case lessonIcon
        }

        init(isBreak: Bool, lessonName: String, lessonTheme: String?, roomName: String?, teacherName: String?, startTime: Date, endTime: Date, lessonNumber: Int?, mode: String?, message: String?, season: String?, nextLessonName: String?, nextRoomName: String?, nextStartTime: Date?, isSubstitution: Bool, isCancelled: Bool, substituteTeacher: String?, currentTime: Date, labels: Labels? = nil, tokenExpirationWarning: String? = nil, tokenExpired: Bool? = nil, compactTimerText: String? = nil, lessonIcon: String? = nil) {
            self.isBreak = isBreak
            self.lessonName = lessonName
            self.lessonTheme = lessonTheme
            self.roomName = roomName
            self.teacherName = teacherName
            self.startTime = startTime
            self.endTime = endTime
            self.lessonNumber = lessonNumber
            self.mode = mode
            self.message = message
            self.season = season
            self.nextLessonName = nextLessonName
            self.nextRoomName = nextRoomName
            self.nextStartTime = nextStartTime
            self.isSubstitution = isSubstitution
            self.isCancelled = isCancelled
            self.substituteTeacher = substituteTeacher
            self.currentTime = currentTime
            self.labels = labels
            self.tokenExpirationWarning = tokenExpirationWarning
            self.tokenExpired = tokenExpired
            self.compactTimerText = compactTimerText
            self.lessonIcon = lessonIcon
        }

        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)

            let isoFormatter = ISO8601DateFormatter()
            isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

            isBreak = try container.decode(Bool.self, forKey: .isBreak)
            lessonName = try container.decode(String.self, forKey: .lessonName)
            lessonTheme = try container.decodeIfPresent(String.self, forKey: .lessonTheme)
            roomName = try container.decodeIfPresent(String.self, forKey: .roomName)
            teacherName = try container.decodeIfPresent(String.self, forKey: .teacherName)

            let startTimeStr = try container.decode(String.self, forKey: .startTime)
            guard let startTimeDate = isoFormatter.date(from: startTimeStr) else {
                throw DecodingError.dataCorruptedError(forKey: .startTime, in: container, debugDescription: "Invalid startTime format: \(startTimeStr)")
            }
            startTime = startTimeDate

            let endTimeStr = try container.decode(String.self, forKey: .endTime)
            guard let endTimeDate = isoFormatter.date(from: endTimeStr) else {
                throw DecodingError.dataCorruptedError(forKey: .endTime, in: container, debugDescription: "Invalid endTime format: \(endTimeStr)")
            }
            endTime = endTimeDate

            lessonNumber = try container.decodeIfPresent(Int.self, forKey: .lessonNumber)
            mode = try container.decodeIfPresent(String.self, forKey: .mode)
            message = try container.decodeIfPresent(String.self, forKey: .message)
            season = try container.decodeIfPresent(String.self, forKey: .season)
            nextLessonName = try container.decodeIfPresent(String.self, forKey: .nextLessonName)
            nextRoomName = try container.decodeIfPresent(String.self, forKey: .nextRoomName)

            if let nextStartTimeStr = try container.decodeIfPresent(String.self, forKey: .nextStartTime) {
                nextStartTime = isoFormatter.date(from: nextStartTimeStr)
            } else {
                nextStartTime = nil
            }

            isSubstitution = try container.decode(Bool.self, forKey: .isSubstitution)
            isCancelled = try container.decode(Bool.self, forKey: .isCancelled)
            substituteTeacher = try container.decodeIfPresent(String.self, forKey: .substituteTeacher)
            labels = try container.decodeIfPresent(Labels.self, forKey: .labels)
            tokenExpirationWarning = try container.decodeIfPresent(String.self, forKey: .tokenExpirationWarning)
            tokenExpired = try container.decodeIfPresent(Bool.self, forKey: .tokenExpired)
            compactTimerText = try container.decodeIfPresent(String.self, forKey: .compactTimerText)
            lessonIcon = try container.decodeIfPresent(String.self, forKey: .lessonIcon)

            let currentTimeStr = try container.decode(String.self, forKey: .currentTime)
            guard let currentTimeDate = isoFormatter.date(from: currentTimeStr) else {
                throw DecodingError.dataCorruptedError(forKey: .currentTime, in: container, debugDescription: "Invalid currentTime format: \(currentTimeStr)")
            }
            currentTime = currentTimeDate
        }

        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: CodingKeys.self)

            let isoFormatter = ISO8601DateFormatter()
            isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

            try container.encode(isBreak, forKey: .isBreak)
            try container.encode(lessonName, forKey: .lessonName)
            try container.encodeIfPresent(lessonTheme, forKey: .lessonTheme)
            try container.encodeIfPresent(roomName, forKey: .roomName)
            try container.encodeIfPresent(teacherName, forKey: .teacherName)

            try container.encode(isoFormatter.string(from: startTime), forKey: .startTime)
            try container.encode(isoFormatter.string(from: endTime), forKey: .endTime)

            try container.encodeIfPresent(lessonNumber, forKey: .lessonNumber)
            try container.encodeIfPresent(mode, forKey: .mode)
            try container.encodeIfPresent(message, forKey: .message)
            try container.encodeIfPresent(season, forKey: .season)
            try container.encodeIfPresent(nextLessonName, forKey: .nextLessonName)
            try container.encodeIfPresent(nextRoomName, forKey: .nextRoomName)

            if let nextStartTime = nextStartTime {
                try container.encode(isoFormatter.string(from: nextStartTime), forKey: .nextStartTime)
            }

            try container.encode(isSubstitution, forKey: .isSubstitution)
            try container.encode(isCancelled, forKey: .isCancelled)
            try container.encodeIfPresent(substituteTeacher, forKey: .substituteTeacher)
            try container.encodeIfPresent(labels, forKey: .labels)
            try container.encodeIfPresent(tokenExpirationWarning, forKey: .tokenExpirationWarning)
            try container.encodeIfPresent(tokenExpired, forKey: .tokenExpired)
            try container.encodeIfPresent(compactTimerText, forKey: .compactTimerText)
            try container.encodeIfPresent(lessonIcon, forKey: .lessonIcon)

            try container.encode(isoFormatter.string(from: currentTime), forKey: .currentTime)
        }
    }

    var studentName: String
    var schoolName: String
}

extension TimetableActivityAttributes.ContentState {
    var timeRemaining: TimeInterval {
        return endTime.timeIntervalSince(currentTime)
    }

    var isBeforeSchool: Bool {
        return currentTime < startTime && !isBreak
    }

    var formattedStartTime: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        formatter.timeZone = TimeZone(identifier: "UTC")
        let adjustedDate = Calendar.current.date(byAdding: .hour, value: 1, to: startTime) ?? startTime
        return formatter.string(from: adjustedDate)
    }

    var formattedEndTime: String {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        formatter.timeZone = TimeZone(identifier: "UTC")
        let adjustedDate = Calendar.current.date(byAdding: .hour, value: 1, to: endTime) ?? endTime
        return formatter.string(from: adjustedDate)
    }

    var formattedNextStartTime: String {
        guard let nextStartTime = nextStartTime else { return "" }
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm"
        formatter.timeZone = TimeZone(identifier: "UTC")
        let adjustedDate = Calendar.current.date(byAdding: .hour, value: 1, to: nextStartTime) ?? nextStartTime
        return formatter.string(from: adjustedDate)
    }

    var timeRemainingText: String {
        let remaining = timeRemaining

        if remaining < 0 {
            return "0:00"
        }

        let hours = Int(remaining) / 3600
        let minutes = (Int(remaining) % 3600) / 60
        let seconds = Int(remaining) % 60

        if hours > 0 {
            return String(format: "%d:%02d:%02d", hours, minutes, seconds)
        } else if minutes > 0 {
            return String(format: "%d:%02d", minutes, seconds)
        } else {
            return String(format: "0:%02d", seconds)
        }
    }

    var seasonalRemainingText: String {
        if let remainingLabel = labels?.remainingLabel, let message = message, !message.isEmpty {
            return "\(remainingLabel): \(message)"
        }

        let remaining = max(0, timeRemaining)
        let hours = Int(remaining) / 3600
        if hours >= 24 {
            let days = hours / 24
            return "Szünetből hátralévő idő: \(days) nap"
        }
        return "Szünetből hátralévő idő: \(hours) óra"
    }

    var seasonalDisplayValue: String {
        if let message = message, !message.isEmpty {
            return message
        }

        let remaining = max(0, timeRemaining)
        let hours = Int(remaining) / 3600
        if hours >= 24 {
            let days = hours / 24
            return "\(days) nap"
        }
        return "\(hours) óra"
    }
}

extension TimetableActivityAttributes.ContentState {
    func toJSON() -> [String: Any] {
        var json: [String: Any] = [
            "isBreak": isBreak,
            "lessonName": lessonName,
            "startTime": ISO8601DateFormatter().string(from: startTime),
            "endTime": ISO8601DateFormatter().string(from: endTime),
            "isSubstitution": isSubstitution,
            "isCancelled": isCancelled,
            "currentTime": ISO8601DateFormatter().string(from: currentTime)
        ]

        if let lessonTheme = lessonTheme {
            json["lessonTheme"] = lessonTheme
        }
        if let roomName = roomName {
            json["roomName"] = roomName
        }
        if let teacherName = teacherName {
            json["teacherName"] = teacherName
        }
        if let lessonNumber = lessonNumber {
            json["lessonNumber"] = lessonNumber
        }
        if let nextLessonName = nextLessonName {
            json["nextLessonName"] = nextLessonName
        }
        if let nextRoomName = nextRoomName {
            json["nextRoomName"] = nextRoomName
        }
        if let nextStartTime = nextStartTime {
            json["nextStartTime"] = ISO8601DateFormatter().string(from: nextStartTime)
        }
        if let substituteTeacher = substituteTeacher {
            json["substituteTeacher"] = substituteTeacher
        }
        if let mode = mode {
            json["mode"] = mode
        }
        if let message = message {
            json["message"] = message
        }
        if let season = season {
            json["season"] = season
        }
        if let tokenExpirationWarning = tokenExpirationWarning {
            json["tokenExpirationWarning"] = tokenExpirationWarning
        }
        if let tokenExpired = tokenExpired {
            json["tokenExpired"] = tokenExpired
        }
        if let compactTimerText = compactTimerText {
            json["compactTimerText"] = compactTimerText
        }
        if let lessonIcon = lessonIcon {
            json["lessonIcon"] = lessonIcon
        }

        return json
    }

    static func fromJSON(_ json: [String: Any]) -> TimetableActivityAttributes.ContentState? {
        let isoFormatter = ISO8601DateFormatter()
        isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]

        guard let isBreak = json["isBreak"] as? Bool,
              let lessonName = json["lessonName"] as? String,
              let startTimeStr = json["startTime"] as? String,
              let endTimeStr = json["endTime"] as? String,
              let isSubstitution = json["isSubstitution"] as? Bool,
              let isCancelled = json["isCancelled"] as? Bool,
              let startTime = isoFormatter.date(from: startTimeStr),
              let endTime = isoFormatter.date(from: endTimeStr) else {
            return nil
        }

        let currentTimeStr = json["currentTime"] as? String
        let currentTime = currentTimeStr.flatMap { isoFormatter.date(from: $0) } ?? Date()

        let nextStartTime: Date?
        if let nextStartTimeStr = json["nextStartTime"] as? String {
            nextStartTime = isoFormatter.date(from: nextStartTimeStr)
        } else {
            nextStartTime = nil
        }

        let labels: Labels?
        if let labelsDict = json["labels"] as? [String: Any] {
            labels = Labels(
                title: labelsDict["title"] as? String,
                timerLabel: labelsDict["timerLabel"] as? String,
                cancelledText: labelsDict["cancelledText"] as? String,
                substitutionText: labelsDict["substitutionText"] as? String,
                roomLabel: labelsDict["roomLabel"] as? String,
                teacherLabel: labelsDict["teacherLabel"] as? String,
                themeLabel: labelsDict["themeLabel"] as? String,
                nextLabel: labelsDict["nextLabel"] as? String,
                firstLessonLabel: labelsDict["firstLessonLabel"] as? String,
                startTimeLabel: labelsDict["startTimeLabel"] as? String,
                lessonNumberLabel: labelsDict["lessonNumberLabel"] as? String,
                nextRoomLabel: labelsDict["nextRoomLabel"] as? String,
                remainingLabel: labelsDict["remainingLabel"] as? String
            )
        } else {
            labels = nil
        }

        return TimetableActivityAttributes.ContentState(
            isBreak: isBreak,
            lessonName: lessonName,
            lessonTheme: json["lessonTheme"] as? String,
            roomName: json["roomName"] as? String,
            teacherName: json["teacherName"] as? String,
            startTime: startTime,
            endTime: endTime,
            lessonNumber: json["lessonNumber"] as? Int,
            mode: json["mode"] as? String,
            message: json["message"] as? String,
            season: json["season"] as? String,
            nextLessonName: json["nextLessonName"] as? String,
            nextRoomName: json["nextRoomName"] as? String,
            nextStartTime: nextStartTime,
            isSubstitution: isSubstitution,
            isCancelled: isCancelled,
            substituteTeacher: json["substituteTeacher"] as? String,
            currentTime: currentTime,
            labels: labels,
            tokenExpirationWarning: json["tokenExpirationWarning"] as? String,
            tokenExpired: json["tokenExpired"] as? Bool,
            compactTimerText: json["compactTimerText"] as? String,
            lessonIcon: json["lessonIcon"] as? String
        )
    }
}
