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 viewedAt: Date?
    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 viewedAt
        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.viewedAt = try container.decodeIfPresent(Date.self, forKey: .viewedAt)
        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?,
        viewedAt: Date?,
        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.viewedAt = viewedAt
        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 avatars: Avatars?
    let stories: [JSONValue]
    let permissions: ChatPermissions?
    let profilePermissions: ChatProfilePermissions?
    let relationship: RelationshipStatus?
    let rating: Double?
    let isOfficial: Bool

    private enum CodingKeys: String, CodingKey {
        case userId
        case login
        case fullName
        case customName
        case bio
        case lastSeen
        case createdAt
        case avatars
        case stories
        case permissions
        case profilePermissions
        case relationship
        case rating
        case isOfficial
        case isVerified
    }

    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.avatars = try container.decodeIfPresent(Avatars.self, forKey: .avatars)
        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)
        let ratingPayload = try container.decodeIfPresent(ChatProfileRatingPayload.self, forKey: .rating)
        self.rating = ratingPayload?.resolvedRating
        let explicitOfficial = try container.decodeIfPresent(Bool.self, forKey: .isOfficial)
        let verifiedFlag = try container.decodeIfPresent(Bool.self, forKey: .isVerified)
        self.isOfficial = explicitOfficial ?? verifiedFlag ?? false
    }
}

private struct ChatProfileRatingPayload: Decodable {
    let status: String?
    let rating: Double?

    private enum CodingKeys: String, CodingKey {
        case rating
        case status
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        status = try container.decodeIfPresent(String.self, forKey: .status)

        if let doubleValue = try? container.decode(Double.self, forKey: .rating) {
            rating = doubleValue
        } else if let stringValue = try? container.decode(String.self, forKey: .rating),
                  let doubleValue = Double(stringValue) {
            rating = doubleValue
        } else if let intValue = try? container.decode(Int.self, forKey: .rating) {
            rating = Double(intValue)
        } else {
            rating = nil
        }
    }

    var resolvedRating: Double? {
        guard status?.lowercased() == "fine" else { return nil }
        return rating
    }
}

extension ChatProfile {
    init(
        userId: String,
        login: String?,
        fullName: String?,
        customName: String?,
        bio: String? = nil,
        lastSeen: Int? = nil,
        createdAt: Date? = nil,
        avatars: Avatars? = nil,
        stories: [JSONValue] = [],
        permissions: ChatPermissions? = nil,
        profilePermissions: ChatProfilePermissions? = nil,
        relationship: RelationshipStatus? = nil,
        rating: Double? = nil,
        isOfficial: Bool = false
    ) {
        self.userId = userId
        self.login = login
        self.fullName = fullName
        self.customName = customName
        self.bio = bio
        self.lastSeen = lastSeen
        self.createdAt = createdAt
        self.avatars = avatars
        self.stories = stories
        self.permissions = permissions
        self.profilePermissions = profilePermissions
        self.relationship = relationship
        self.rating = rating
        self.isOfficial = isOfficial
    }
}

struct AvatarInfo: Decodable {
    let fileId: String
    let mime: String?
    let size: Int?
    let width: Int?
    let height: Int?
    let createdAt: Date?
}

struct Avatars: Decodable {
    let current: AvatarInfo?
}

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 isTargetInContactsOfCurrentUser: Bool
    let isCurrentUserInContactsOfTarget: Bool
    let isTargetUserBlockedByCurrentUser: Bool
    let isCurrentUserInBlacklistOfTarget: Bool

    private enum CodingKeys: String, CodingKey {
        case isTargetInContactsOfCurrentUser
        case isCurrentUserInContactsOfTarget
        case isTargetUserBlockedByCurrentUser
        case isCurrentUserInBlacklistOfTarget
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.isTargetInContactsOfCurrentUser = try container.decodeIfPresent(Bool.self, forKey: .isTargetInContactsOfCurrentUser) ?? false
        self.isCurrentUserInContactsOfTarget = try container.decodeIfPresent(Bool.self, forKey: .isCurrentUserInContactsOfTarget) ?? false
        self.isTargetUserBlockedByCurrentUser = try container.decodeIfPresent(Bool.self, forKey: .isTargetUserBlockedByCurrentUser) ?? false
        self.isCurrentUserInBlacklistOfTarget = try container.decodeIfPresent(Bool.self, forKey: .isCurrentUserInBlacklistOfTarget) ?? false
    }
}

extension RelationshipStatus {
    init(
        isTargetInContactsOfCurrentUser: Bool,
        isCurrentUserInContactsOfTarget: Bool,
        isTargetUserBlockedByCurrentUser: Bool,
        isCurrentUserInBlacklistOfTarget: Bool
    ) {
        self.isTargetInContactsOfCurrentUser = isTargetInContactsOfCurrentUser
        self.isCurrentUserInContactsOfTarget = isCurrentUserInContactsOfTarget
        self.isTargetUserBlockedByCurrentUser = isTargetUserBlockedByCurrentUser
        self.isCurrentUserInBlacklistOfTarget = isCurrentUserInBlacklistOfTarget
    }
}

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)"))
    }
}
