import Foundation
import SwiftData

public struct NotificationItem: Equatable, Hashable, Codable, Expirable {
    public var id: String
    public var notificationType: NotificationType
    public var createdAt: Date
    public var formattedCreateAt: String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .full
        return formatter.localizedString(for: createdAt, relativeTo: Date.now)
    }

    public var isRead: Bool
    public var userProfiles: [MiniProfile]
    public var totalUsers: Int
    public var reactContent: MiniClipOrPlaylist?
    public var message: String?

    public init(
        notificationType: NotificationType,
        id: String,
        createdAt: Date,
        isRead: Bool,
        userProfiles: [MiniProfile],
        totalUsers: Int,
        reactContent: MiniClipOrPlaylist?,
        message: String
    ) {
        self.notificationType = notificationType
        self.id = id
        self.createdAt = createdAt
        self.isRead = isRead
        self.userProfiles = userProfiles
        self.totalUsers = totalUsers
        self.reactContent = reactContent
        self.message = message
    }

    init?(_ remote: Components.Schemas.UserNotificationSchema.notificationsPayloadPayload?) throws {
        guard let knownRemote = remote?.value1 else { return nil }
        switch knownRemote {
        case .clip_like(let notification),
             .clip_unlike(let notification),
             .scene_like(let notification),
             .scene_unlike(let notification),
             .playlist_like(let notification),
             .playlist_unlike(let notification):
            guard let remoteNotificationType = notification.notification_type?.rawValue else { return nil }
            self.notificationType = .init(rawValue: remoteNotificationType) ?? .restrictedByDefault
            self.id = notification.id
            self.createdAt = notification.created_at
            self.isRead = notification.is_read ?? false
            self.userProfiles = try notification.user_profiles.map(MiniProfile.init)
            self.totalUsers = notification.total_users ?? 0
            self.reactContent = try MiniClipOrPlaylist(notification.react_content)
            self.message = nil

        case .follow(let notification), .unfollow(let notification):
            guard let remoteNotificationType = notification.notification_type?.rawValue else { return nil }
            self.notificationType = .init(rawValue: remoteNotificationType) ?? .restrictedByDefault
            self.id = notification.id
            self.createdAt = notification.created_at
            self.isRead = notification.is_read ?? false
            self.userProfiles = try notification.user_profiles.map(MiniProfile.init)
            self.totalUsers = notification.total_users ?? 0
            self.reactContent = nil
            self.message = nil

        case .announcement(let notification):
            guard let remoteNotificationType = notification.notification_type?.rawValue else { return nil }
            self.notificationType = .init(rawValue: remoteNotificationType) ?? .restrictedByDefault
            self.id = notification.id
            self.createdAt = notification.created_at
            self.isRead = notification.is_read ?? false
            self.userProfiles = []
            self.totalUsers = 0
            self.reactContent = try {
                guard let clip = notification.clip else { return nil }
                return try MiniClipOrPlaylist(clip)
            }()
            self.message = notification.message
        }
    }

    enum CodingKeys: String, CodingKey {
        case notificationType = "notification_type"
        case id
        case createdAt = "created_at"
        case isRead = "is_read"
        case userProfiles = "user_profiles"
        case totalUsers = "total_users"
        case reactContent = "react_content"
        case message
    }

    public init(from decoder: any Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        notificationType = (try? container.decode(NotificationType.self, forKey: .notificationType)) ?? .restrictedByDefault
        id = try container.decode(String.self, forKey: .id)
        createdAt = try container.decode(Date.self, forKey: .createdAt)
        isRead = try container.decode(Bool.self, forKey: .isRead)
        userProfiles = try container.decodeIfPresent([MiniProfile].self, forKey: .userProfiles) ?? []
        totalUsers = try container.decodeIfPresent(Int.self, forKey: .totalUsers) ?? 0
        reactContent = try container.decodeIfPresent(MiniClipOrPlaylist.self, forKey: .reactContent)
        message = try container.decodeIfPresent(String.self, forKey: .message)
    }

    public func encode(to encoder: any Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(notificationType, forKey: .notificationType)
        try container.encode(id, forKey: .id)
        try container.encode(createdAt, forKey: .createdAt)
        try container.encode(isRead, forKey: .isRead)
        try container.encode(userProfiles, forKey: .userProfiles)
        try container.encode(totalUsers, forKey: .totalUsers)
        try container.encodeIfPresent(reactContent, forKey: .reactContent)
        try container.encodeIfPresent(message, forKey: .message)
    }

    public var isExpired: Bool {
        let expiryDate = Calendar.current.date(byAdding: .day, value: 60, to: createdAt)!
        return Date() > expiryDate
    }
}
