//
//  MessageCenterModels.swift
//  suixinkan
//

import Foundation

/// 可解码的 JSON 值，用于承接消息 `extra_data` 原始结构。
enum MessageJSONValue: Decodable, Hashable, Sendable {
    case string(String)
    case number(Double)
    case bool(Bool)
    case object([String: MessageJSONValue])
    case array([MessageJSONValue])
    case null

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if container.decodeNil() {
            self = .null
        } else if let value = try? container.decode(Bool.self) {
            self = .bool(value)
        } else if let value = try? container.decode(Double.self) {
            self = .number(value)
        } else if let value = try? container.decode(String.self) {
            self = .string(value)
        } else if let value = try? container.decode([MessageJSONValue].self) {
            self = .array(value)
        } else {
            self = .object(try container.decode([String: MessageJSONValue].self))
        }
    }
}

/// 消息未读数量响应，用于同步首页红点和桌面图标角标。
struct MessageUnreadCountResponse: Decodable, Equatable, Sendable {
    let unreadCount: Int

    enum CodingKeys: String, CodingKey {
        case unreadCount = "unread_count"
    }

    init(unreadCount: Int = 0) {
        self.unreadCount = max(unreadCount, 0)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        unreadCount = max(try container.decodeLossyInt(forKey: .unreadCount) ?? 0, 0)
    }
}

/// 全部标记已读响应，包含本次更新数量和操作后的未读数量。
struct MessageReadAllResponse: Decodable, Equatable, Sendable {
    let updatedCount: Int
    let unreadCount: Int

    enum CodingKeys: String, CodingKey {
        case updatedCount = "updated_count"
        case unreadCount = "unread_count"
    }

    init(updatedCount: Int = 0, unreadCount: Int = 0) {
        self.updatedCount = max(updatedCount, 0)
        self.unreadCount = max(unreadCount, 0)
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        updatedCount = max(try container.decodeLossyInt(forKey: .updatedCount) ?? 0, 0)
        unreadCount = max(try container.decodeLossyInt(forKey: .unreadCount) ?? 0, 0)
    }
}

/// 消息中心列表响应，对齐 Android `MsgResponse`。
struct MessageListResponse: Decodable, Equatable, Sendable {
    let hasMore: Bool
    let lastId: Int
    let items: [MessageItem]
    let isRefresh: Bool

    enum CodingKeys: String, CodingKey {
        case hasMore = "has_more"
        case lastId = "last_id"
        case items
        case isRefresh = "is_refresh"
    }

    init(hasMore: Bool = false, lastId: Int = 0, items: [MessageItem] = [], isRefresh: Bool = false) {
        self.hasMore = hasMore
        self.lastId = lastId
        self.items = items
        self.isRefresh = isRefresh
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        hasMore = try container.decodeLossyBool(forKey: .hasMore) ?? false
        lastId = try container.decodeLossyInt(forKey: .lastId) ?? 0
        items = (try? container.decodeIfPresent([MessageItem].self, forKey: .items)) ?? []
        isRefresh = try container.decodeLossyBool(forKey: .isRefresh) ?? false
    }
}

/// 单条消息实体，对齐 Android `MsgEntity`。
struct MessageItem: Decodable, Hashable, Sendable {
    let id: Int
    let receiverId: Int
    let receiverType: String
    let type: Int
    let typeName: String
    let title: String
    let content: String
    let pushAt: String
    let isRead: Bool
    let readAt: String
    let pushChannel: Int
    let pushChannelName: String
    let extraData: [String: MessageJSONValue]?
    let createdAt: String
    let updatedAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case receiverId = "receiver_id"
        case receiverType = "receiver_type"
        case type
        case typeName = "type_name"
        case title
        case content
        case pushAt = "push_at"
        case isRead = "is_read"
        case readAt = "read_at"
        case pushChannel = "push_channel"
        case pushChannelName = "push_channel_name"
        case extraData = "extra_data"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }

    init(
        id: Int = 0,
        receiverId: Int = 0,
        receiverType: String = "",
        type: Int = 0,
        typeName: String = "",
        title: String = "",
        content: String = "",
        pushAt: String = "",
        isRead: Bool = false,
        readAt: String = "",
        pushChannel: Int = 0,
        pushChannelName: String = "",
        extraData: [String: MessageJSONValue]? = nil,
        createdAt: String = "",
        updatedAt: String = ""
    ) {
        self.id = id
        self.receiverId = receiverId
        self.receiverType = receiverType
        self.type = type
        self.typeName = typeName
        self.title = title
        self.content = content
        self.pushAt = pushAt
        self.isRead = isRead
        self.readAt = readAt
        self.pushChannel = pushChannel
        self.pushChannelName = pushChannelName
        self.extraData = extraData
        self.createdAt = createdAt
        self.updatedAt = updatedAt
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        receiverId = try container.decodeLossyInt(forKey: .receiverId) ?? 0
        receiverType = try container.decodeLossyString(forKey: .receiverType)
        type = try container.decodeLossyInt(forKey: .type) ?? 0
        typeName = try container.decodeLossyString(forKey: .typeName)
        title = try container.decodeLossyString(forKey: .title)
        content = try container.decodeLossyString(forKey: .content)
        pushAt = try container.decodeLossyString(forKey: .pushAt)
        isRead = try container.decodeLossyBool(forKey: .isRead) ?? false
        readAt = try container.decodeLossyString(forKey: .readAt)
        pushChannel = try container.decodeLossyInt(forKey: .pushChannel) ?? 0
        pushChannelName = try container.decodeLossyString(forKey: .pushChannelName)
        extraData = try? container.decodeIfPresent([String: MessageJSONValue].self, forKey: .extraData)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        updatedAt = try container.decodeLossyString(forKey: .updatedAt)
    }

    /// Android 列表标题使用 `type_name`。
    var displayTitle: String {
        typeName.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    /// Android 列表优先展示 `push_at`，否则格式化 `created_at`。
    var displayTime: String {
        let pushTime = pushAt.trimmingCharacters(in: .whitespacesAndNewlines)
        if !pushTime.isEmpty { return pushTime }
        return MessageDateFormatter.formatDateTime(createdAt) ?? createdAt
    }

    /// 返回已读状态的消息副本。
    func markedRead() -> MessageItem {
        MessageItem(
            id: id,
            receiverId: receiverId,
            receiverType: receiverType,
            type: type,
            typeName: typeName,
            title: title,
            content: content,
            pushAt: pushAt,
            isRead: true,
            readAt: readAt,
            pushChannel: pushChannel,
            pushChannelName: pushChannelName,
            extraData: extraData,
            createdAt: createdAt,
            updatedAt: updatedAt
        )
    }
}

/// 消息删除请求体，对齐 Android `MsgDeleteRequest`。
struct MessageDeleteRequest: Encodable, Sendable {
    let id: Int
}

/// 消息中心日期显示工具，对齐 Android `DateUtils.formatDateTime` 的用户可见规则。
enum MessageDateFormatter {
    private static let calendar = Calendar.current

    static func formatDateTime(_ input: String?) -> String? {
        guard let input = input?.trimmingCharacters(in: .whitespacesAndNewlines), !input.isEmpty else { return nil }
        guard let date = parseDate(input) else { return nil }

        let today = Date()
        let startOfInput = calendar.startOfDay(for: date)
        let startOfToday = calendar.startOfDay(for: today)
        let dayDifference = calendar.dateComponents([.day], from: startOfInput, to: startOfToday).day ?? 0
        let inputComponents = calendar.dateComponents([.year, .month, .day, .weekday], from: date)
        let todayComponents = calendar.dateComponents([.year, .month], from: today)

        if dayDifference == 0 {
            return "今天"
        }
        if (1 ... 6).contains(dayDifference), let weekday = inputComponents.weekday {
            return weekdayName(weekday)
        }
        if inputComponents.year == todayComponents.year, inputComponents.month == todayComponents.month {
            return "本月\(inputComponents.day ?? 0)日"
        }
        if inputComponents.year == todayComponents.year {
            return "\(inputComponents.month ?? 0)月\(inputComponents.day ?? 0)日"
        }
        return "\(inputComponents.year ?? 0)年\(inputComponents.month ?? 0)月\(inputComponents.day ?? 0)日"
    }

    private static func parseDate(_ input: String) -> Date? {
        if let timestamp = Double(input) {
            let seconds = timestamp > 1_000_000_000_000 ? timestamp / 1000 : timestamp
            return Date(timeIntervalSince1970: seconds)
        }

        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = .current
        for format in ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd"] {
            formatter.dateFormat = format
            if let date = formatter.date(from: input) {
                return date
            }
        }
        return nil
    }

    private static func weekdayName(_ weekday: Int) -> String {
        switch weekday {
        case 1: return "周日"
        case 2: return "周一"
        case 3: return "周二"
        case 4: return "周三"
        case 5: return "周四"
        case 6: return "周五"
        case 7: return "周六"
        default: return ""
        }
    }
}

private extension KeyedDecodingContainer {
    func decodeLossyString(forKey key: Key) throws -> String {
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return String(value)
        }
        if let value = try? decodeIfPresent(Double.self, forKey: key) {
            return String(value)
        }
        if let value = try? decodeIfPresent(Bool.self, forKey: key) {
            return value ? "true" : "false"
        }
        return ""
    }

    func decodeLossyInt(forKey key: Key) throws -> Int? {
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Double.self, forKey: key) {
            return Int(value)
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
            if let intValue = Int(text) { return intValue }
            if let doubleValue = Double(text) { return Int(doubleValue) }
        }
        return nil
    }

    func decodeLossyBool(forKey key: Key) throws -> Bool? {
        if let value = try? decodeIfPresent(Bool.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return value != 0
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            let text = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
            if ["true", "1", "yes"].contains(text) { return true }
            if ["false", "0", "no"].contains(text) { return false }
        }
        return nil
    }
}
