import Foundation struct PrivateChatListData: Decodable { let items: [PrivateChatListItem] let hasMore: Bool } struct PrivateChatHistoryData: Decodable { let items: [MessageItem] let hasMore: Bool } struct PrivateChatCreateData: Decodable { let chatId: String let chatType: PrivateChatListItem.ChatType let status: String let message: String private enum CodingKeys: String, CodingKey { case chatId case chatType case status case message } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.chatId = try container.decodeFlexibleString(forKey: .chatId) self.chatType = try container.decode(PrivateChatListItem.ChatType.self, forKey: .chatType) self.status = try container.decode(String.self, forKey: .status) self.message = try container.decode(String.self, forKey: .message) } } struct PrivateMessageSendData: Decodable { let messageId: String let chatId: String let createdAt: Date? private enum CodingKeys: String, CodingKey { case messageId case chatId case createdAt } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.messageId = try container.decodeFlexibleString(forKey: .messageId) self.chatId = try container.decodeFlexibleString(forKey: .chatId) self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt) } } struct PrivateChatListItem: Decodable, Identifiable { enum ChatType: String, Decodable { case `self` case privateChat = "private" case unknown init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let rawValue = try container.decode(String.self) self = ChatType(rawValue: rawValue) ?? .unknown } } let chatId: String let chatType: ChatType let chatData: ChatProfile? let lastMessage: MessageItem? let createdAt: Date? let unreadCount: Int var id: String { chatId } } struct MessageItem: Decodable, Identifiable { let messageId: String let messageType: String let chatId: String let senderId: String let senderData: ChatProfile? let content: String? let mediaLink: String? let isViewed: Bool? let createdAt: Date? let updatedAt: Date? let forwardMetadata: ForwardMetadata? var id: String { messageId } private enum CodingKeys: String, CodingKey { case messageId case messageType case chatId case senderId case senderData case content case mediaLink case isViewed case createdAt case updatedAt case forwardMetadata } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.messageId = try container.decodeFlexibleString(forKey: .messageId) self.messageType = try container.decodeFlexibleStringOrArray(forKey: .messageType) self.chatId = try container.decodeFlexibleString(forKey: .chatId) self.senderId = try container.decodeFlexibleString(forKey: .senderId) self.senderData = try container.decodeIfPresent(ChatProfile.self, forKey: .senderData) self.content = try container.decodeIfPresent(String.self, forKey: .content) self.mediaLink = try container.decodeIfPresent(String.self, forKey: .mediaLink) self.isViewed = try container.decodeIfPresent(Bool.self, forKey: .isViewed) self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt) self.updatedAt = try container.decodeIfPresent(Date.self, forKey: .updatedAt) self.forwardMetadata = try container.decodeIfPresent(ForwardMetadata.self, forKey: .forwardMetadata) } init( messageId: String, messageType: String, chatId: String, senderId: String, senderData: ChatProfile?, content: String?, mediaLink: String?, isViewed: Bool?, createdAt: Date?, updatedAt: Date?, forwardMetadata: ForwardMetadata? ) { self.messageId = messageId self.messageType = messageType self.chatId = chatId self.senderId = senderId self.senderData = senderData self.content = content self.mediaLink = mediaLink self.isViewed = isViewed self.createdAt = createdAt self.updatedAt = updatedAt self.forwardMetadata = forwardMetadata } } struct ForwardMetadata: Decodable { let forwardType: String? let forwardSenderId: String? let forwardMessageId: String? let forwardChatData: ChatProfile? private enum CodingKeys: String, CodingKey { case forwardType case forwardSenderId case forwardMessageId case forwardChatData } } struct ChatProfile: Decodable { let userId: String let login: String? let fullName: String? let customName: String? let bio: String? let lastSeen: Int? let createdAt: Date? let stories: [JSONValue] let permissions: ChatPermissions? let profilePermissions: ChatProfilePermissions? let relationship: RelationshipStatus? private enum CodingKeys: String, CodingKey { case userId case login case fullName case customName case bio case lastSeen case createdAt case stories case permissions case profilePermissions case relationship } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.userId = try container.decodeFlexibleString(forKey: .userId) self.login = try container.decodeIfPresent(String.self, forKey: .login) self.fullName = try container.decodeIfPresent(String.self, forKey: .fullName) self.customName = try container.decodeIfPresent(String.self, forKey: .customName) self.bio = try container.decodeIfPresent(String.self, forKey: .bio) self.lastSeen = try container.decodeIfPresent(Int.self, forKey: .lastSeen) self.createdAt = try container.decodeIfPresent(Date.self, forKey: .createdAt) self.stories = try container.decodeIfPresent([JSONValue].self, forKey: .stories) ?? [] self.permissions = try container.decodeIfPresent(ChatPermissions.self, forKey: .permissions) self.profilePermissions = try container.decodeIfPresent(ChatProfilePermissions.self, forKey: .profilePermissions) self.relationship = try container.decodeIfPresent(RelationshipStatus.self, forKey: .relationship) } } extension ChatProfile { init( userId: String, login: String?, fullName: String?, customName: String?, bio: String? = nil, lastSeen: Int? = nil, createdAt: Date? = nil, stories: [JSONValue] = [], permissions: ChatPermissions? = nil, profilePermissions: ChatProfilePermissions? = nil, relationship: RelationshipStatus? = nil ) { self.userId = userId self.login = login self.fullName = fullName self.customName = customName self.bio = bio self.lastSeen = lastSeen self.createdAt = createdAt self.stories = stories self.permissions = permissions self.profilePermissions = profilePermissions self.relationship = relationship } } struct ChatPermissions: Decodable { let youCanSendMessage: Bool let youCanPublicInvitePermission: Bool let youCanGroupInvitePermission: Bool let youCanCallPermission: Bool } struct ChatProfilePermissions: Decodable { let isSearchable: Bool? let allowMessageForwarding: Bool let allowMessagesFromNonContacts: Bool let allowServerChats: Bool let forceAutoDeleteMessagesInPrivate: Bool let maxMessageAutoDeleteSeconds: Int? } struct RelationshipStatus: Decodable { let isCurrentUserInContactsOfTarget: Bool let isTargetUserBlockedByCurrentUser: Bool let isCurrentUserInBlacklistOfTarget: Bool } enum JSONValue: Decodable { case string(String) case int(Int) case double(Double) case bool(Bool) case array([JSONValue]) case object([String: JSONValue]) case null init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if container.decodeNil() { self = .null return } if let value = try? container.decode(Bool.self) { self = .bool(value) return } if let value = try? container.decode(Int.self) { self = .int(value) return } if let value = try? container.decode(Double.self) { self = .double(value) return } if let value = try? container.decode(String.self) { self = .string(value) return } if let value = try? container.decode([JSONValue].self) { self = .array(value) return } if let value = try? container.decode([String: JSONValue].self) { self = .object(value) return } throw DecodingError.dataCorruptedError(in: container, debugDescription: "Не удалось декодировать значение JSONValue") } } private extension KeyedDecodingContainer { func decodeFlexibleString(forKey key: Key) throws -> String { if let string = try? decode(String.self, forKey: key) { return string } if let int = try? decode(Int.self, forKey: key) { return String(int) } if let double = try? decode(Double.self, forKey: key) { return String(double) } throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: codingPath + [key], debugDescription: "Expected to decode String or number for key \(key.stringValue)")) } func decodeFlexibleStringOrArray(forKey key: Key) throws -> String { if let string = try? decode(String.self, forKey: key) { return string } if let stringArray = try? decode([String].self, forKey: key), let first = stringArray.first { return first } if let intArray = try? decode([Int].self, forKey: key), let first = intArray.first { return String(first) } if let doubleArray = try? decode([Double].self, forKey: key), let first = doubleArray.first { return String(first) } throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: codingPath + [key], debugDescription: "Expected to decode String or array for key \(key.stringValue)")) } }