import Foundation

/**
    SunoModelWrapper: Wrapper to define SunoModel defined here to avoid circular dependency.
    We should move to a shared "Models" module that acts as a core module instead of having models defined twice
 */

public struct SunoModelMetaData: Equatable, Codable {
    public enum MarketingLevelUnderstanding: String {
        case previousToV4
        case v3Dot5
        case v4
        case auk
        case bluejay
        case v5
    }

    public struct ModelBadgeStyle: Equatable, Codable {
        public struct ColorStyle: Equatable, Codable {
            public let textColor: String
            public let backgroundColor: String
            public let borderColor: String

            public init(textColor: String, backgroundColor: String, borderColor: String) {
                self.textColor = textColor
                self.backgroundColor = backgroundColor
                self.borderColor = borderColor
            }
        }

        public let displayName: String
        public let light: ColorStyle
        public let dark: ColorStyle

        public init(displayName: String, light: ColorStyle, dark: ColorStyle) {
            self.displayName = displayName
            self.light = light
            self.dark = dark
        }
    }

    /**
        Models do come back from backend on `Me` however
        we are using the hard coded versions here since
        there is no contract yet established between mobile and
        backend on how to treat the model identifiers in the array.

        Subscribers will default to v4 and through understanding of billing info's
        sunoModelUserAccess will determine which model is used otherwise.
     */

    static let modelV3_5: SunoModelMetaData = .init(
        id: "b8f595a3-f331-4424-a349-0dee6d894e3b",
        name: "v3.5",
        majorVersion: 3,
        externalKey: "chirp-v3-5-tau"
    )

    static let modelV4: SunoModelMetaData = .init(
        id: "8140f9b4-d0c0-4281-9d92-a27600bce307",
        name: "v4",
        majorVersion: 4,
        externalKey: "chirp-v4-tau"
    )

    public static let modelV4_5: SunoModelMetaData = .init(
        id: "1ef9ac63-e6f7-43e7-b190-3ec7beaaceef",
        name: "v4.5",
        majorVersion: 5,
        externalKey: "chirp-auk"
    )

    public static let modelV5: SunoModelMetaData = .init(
        id: "a34a10e8-7b15-4de1-956f-a995a0fbfe58",
        name: "v5",
        majorVersion: 5,
        externalKey: "chirp-crow"
    )

    public static var modelDefault: SunoModelMetaData {
        return modelV3_5
    }

    public var id: String
    public var name: String
    public var majorVersion: Int
    public var externalKey: String
    public var description: String?

    public var canUse: Bool?
    public var maxLengths: MaxLengths?

    // For display
    // TODO: We really should get these tags from the backend
    public var isPro: Bool
    public var isNew: Bool
    public var isBeta: Bool
    public var songsLeft: Int?

    public var isDefaultModel: Bool?
    public var isDefaultFreeModel: Bool?
    public var badges: [ModelBadge]?
    public var modelSelectorBadgeStyle: ModelBadgeStyle?

    public init(
        id: String,
        name: String,
        majorVersion: Int,
        externalKey: String,
        description: String? = nil,
        canUse: Bool? = nil,
        maxLengths: MaxLengths? = nil,
        isPro: Bool = false,
        isNew: Bool = false,
        isBeta: Bool = false,
        songsLeft: Int? = nil,
        isDefaultModel: Bool? = nil,
        isDefaultFreeModel: Bool? = nil,
        badges: [ModelBadge]? = nil,
        modelSelectorBadgeStyle: ModelBadgeStyle? = nil
    ) {
        self.id = id
        self.name = name
        self.majorVersion = majorVersion
        self.externalKey = externalKey
        self.description = description
        self.canUse = canUse
        self.maxLengths = maxLengths
        self.isPro = isPro
        self.isNew = isNew
        self.isBeta = isBeta
        self.songsLeft = songsLeft
        self.isDefaultModel = isDefaultModel
        self.isDefaultFreeModel = isDefaultFreeModel
        self.badges = badges
        self.modelSelectorBadgeStyle = modelSelectorBadgeStyle
    }

    public var asJSONData: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }

    func getExternalKey(_ isAudioUpload: Bool) -> String {
        // Appends -upload to the name when using `chirp-v3-5`
        if externalKey.hasPrefix("chirp-v3-5") {
            return isAudioUpload ? "chirp-v3-5-upload" : "chirp-v3-5-tau"
            // Adds -tau to major version 4
        } else if majorVersion == 4 {
            return "chirp-v4-tau"
        } else {
            return externalKey
        }
    }

    public static func fromDataOrDefault(_ data: Data?) -> SunoModelMetaData {
        guard let data else { return .modelV3_5 }
        let decoder = JSONDecoder()
        return (try? decoder.decode(SunoModelMetaData.self, from: data)) ?? .modelV3_5
    }

    public var marketingLevelUnderstanding: MarketingLevelUnderstanding {
        switch majorVersion {
        case 4:
            return .v4

        case 5:
            /// Bluejay, Auk and Crow have the same major version, so we have to string match here...
            if externalKey == "chirp-crow" || name == "v5" {
                return .v5
            } else if externalKey == "chirp-bluejay" || name == "v4.5+" {
                return .bluejay
            } else {
                return .auk
            }

        default:
            return .previousToV4
        }
    }

    public struct MaxLengths: Codable, Equatable {
        public let gptDescriptionPrompt: Int
        public let negativeTags: Int
        public let prompt: Int
        public let tags: Int
        public let title: Int

        public init(gptDescriptionPrompt: Int, negativeTags: Int, prompt: Int, tags: Int, title: Int) {
            self.gptDescriptionPrompt = gptDescriptionPrompt
            self.negativeTags = negativeTags
            self.prompt = prompt
            self.tags = tags
            self.title = title
        }
    }
}
