import Foundation
import GenAPI
import Localization

public struct Prompt: Equatable, Codable {
    // TODO: Use API endpoint when available

    private(set) var adjectives = [
        L10n.APIClient.acoustic,
        L10n.APIClient.aggressive,
        L10n.APIClient.anthemic,
        L10n.APIClient.atmospheric,
        L10n.APIClient.bouncy,
        L10n.APIClient.chill,
        L10n.APIClient.dark,
        L10n.APIClient.dreamy,
        L10n.APIClient.electronic,
        L10n.APIClient.emotional,
        L10n.APIClient.epic,
        L10n.APIClient.experimental,
        L10n.APIClient.futuristic,
        L10n.APIClient.groovy,
        L10n.APIClient.heartfelt,
        L10n.APIClient.infectious,
        L10n.APIClient.melodic,
        L10n.APIClient.mellow,
        L10n.APIClient.powerful,
        L10n.APIClient.psychedelic,
        L10n.APIClient.romantic,
        L10n.APIClient.smooth,
        L10n.APIClient.syncopated,
        L10n.APIClient.uplifting,
        "",
    ]

    public private(set) var genres = [
        L10n.APIClient.afrobeat,
        L10n.APIClient.anime,
        L10n.APIClient.ballad,
        L10n.APIClient.bedroomPop,
        L10n.APIClient.bluegrass,
        L10n.APIClient.blues,
        L10n.APIClient.classical,
        L10n.APIClient.country,
        L10n.APIClient.cumbia,
        L10n.APIClient.dance,
        L10n.APIClient.dancepop,
        L10n.APIClient.deltaBlues,
        L10n.APIClient.electropop,
        L10n.APIClient.disco,
        L10n.APIClient.dreamPop,
        L10n.APIClient.drumAndBass,
        L10n.APIClient.edm,
        L10n.APIClient.emo,
        L10n.APIClient.folk,
        L10n.APIClient.funk,
        L10n.APIClient.futureBass,
        L10n.APIClient.gospel,
        L10n.APIClient.grunge,
        L10n.APIClient.grime,
        L10n.APIClient.hipHop,
        L10n.APIClient.house,
        L10n.APIClient.indie,
        L10n.APIClient.jPop,
        L10n.APIClient.jazz,
        L10n.APIClient.kPop,
        L10n.APIClient.kidsMusic,
        L10n.APIClient.metal,
        L10n.APIClient.newJackSwing,
        L10n.APIClient.newWave,
        L10n.APIClient.opera,
        L10n.APIClient.pop,
        L10n.APIClient.punk,
        L10n.APIClient.raga,
        L10n.APIClient.rap,
        L10n.APIClient.reggae,
        L10n.APIClient.reggaeton,
        L10n.APIClient.rock,
        L10n.APIClient.rumba,
        L10n.APIClient.salsa,
        L10n.APIClient.samba,
        L10n.APIClient.sertanejo,
        L10n.APIClient.soul,
        L10n.APIClient.synthpop,
        L10n.APIClient.swing,
        L10n.APIClient.synthwave,
        L10n.APIClient.techno,
        L10n.APIClient.trap,
        L10n.APIClient.ukGarage,
    ]

    private(set) var topics = [
        L10n.APIClient.aBadBreakup,
        L10n.APIClient.findingLoveOnARainyDay,
        L10n.APIClient.aCozyRainyDay,
        L10n.APIClient.dancingAllNightLong,
        L10n.APIClient.dancingWithYouForTheLastTime,
        L10n.APIClient.notBeingAbleToWaitToSeeYouAgain,
        L10n.APIClient.howYoureAlwaysThereForMe,
        L10n.APIClient.whenYoureNotAround,
        L10n.APIClient.aFadedPhotoOnTheMantel,
        L10n.APIClient.aLiteralBanana,
        L10n.APIClient.wantingToBeWithYou,
        L10n.APIClient.writingAFaceMeltingGuitarSolo,
        L10n.APIClient.thePlaceWhereWeUsedToGo,
        L10n.APIClient.beingTrappedInAnAISongFactoryHelp,
    ]

    public enum TaskType: String, Codable {
        case extend
        case uploadExtend = "upload_extend"
        case infill
        case infillIntro = "infill_intro"
        case infillOutro = "infill_outro"
        case artistConsistency = "artist_consistency"
        case cover
        case imageToSong = "image_to_song"
        case videoToSong = "video_to_song"
        case concat
        case concatInfilling = "concat_infilling"
        case upsample
        case twitterToSong = "twitter_to_song"
    }

    // Custom
    public var title: String
    public var lyrics: String
    public var styles: String
    public var excludeStyles: String
    public var instrumental: Bool

    // The prompt entered by user in simple mode text prompt
    public var text: String

    // Covers
    // The root clip to base the cover off of
    public var coverClipId: String?

    // Audio
    public var continueClipId: UploadRequest.ID?
    public var continueAt: TimeInterval?
    public var includeHistory: Bool?
    public var audioRecording: AudioRecording?

    public var generationType: GenerationType

    // hCatpcha Token, if required
    public var token: String?

    public var task: TaskType?

    public var isCustomized: Bool {
        !title.isEmpty || !lyrics.isEmpty || instrumental == true
    }

    // Remix
    public var isRemix: Bool?

    // Additional options / metadata
    public var createMode: String?
    public var lyricsModel: String?
    public var audioWeight: Double?
    public var styleWeight: Double?
    public var weirdnessConstraint: Double?

    // MARK: - Public

    public init(
        title: String = "",
        lyrics: String = "",
        instrumental: Bool = false,
        styles: String = "",
        excludeStyles: String = "",
        text: String = "",
        coverClipId: String? = nil,
        continueClipId: UploadRequest.ID? = nil,
        continueAt: TimeInterval? = nil,
        includeHistory: Bool? = nil,
        audioRecording: AudioRecording? = nil,
        generationType: GenerationType = .text,
        token: String? = nil,
        task: TaskType? = nil,
        isRemix: Bool? = nil,
        createMode: String? = nil,
        lyricsModel: String? = nil,
        audioWeight: Double? = nil,
        styleWeight: Double? = nil,
        weirdnessConstraint: Double? = nil
    ) {
        self.title = title
        self.lyrics = lyrics
        self.instrumental = instrumental
        self.styles = styles
        self.excludeStyles = excludeStyles
        self.text = text
        self.coverClipId = coverClipId
        self.continueClipId = continueClipId
        self.continueAt = continueAt
        self.includeHistory = includeHistory
        self.audioRecording = audioRecording
        self.generationType = generationType
        self.token = token
        self.task = task
        self.isRemix = isRemix
        self.createMode = createMode
        self.lyricsModel = lyricsModel
        self.audioWeight = audioWeight
        self.styleWeight = styleWeight
        self.weirdnessConstraint = weirdnessConstraint
    }

    // MARK: - v0

    public func getRandomPrompt() -> String {
        let adjective = getRandomElement(adjectives).lowercased()
        let genre = getRandomElement(genres).lowercased()
        let topic = getRandomElement(topics).lowercased()
        return L10n.APIClient.randomPrompt(adjective, genre, topic)
    }

    public func getRandomPrompts(total: Int) -> [String] {
        var prompts: [String] = []
        for _ in 1 ... total {
            prompts.append(getRandomPrompt())
        }
        return prompts
    }

    // MARK: - Private

    private func getRandomElement(_ items: [String]) -> String {
        let index = Int.random(in: 0 ..< items.count)
        return items[index]
    }

    // MARK: - Debug

    public func debugPrint() -> String {
        """
        ------
        [PROMPT]
        Title: \(title)
        Lyrics: \(lyrics)
        Instrumental: \(instrumental)
        Styles: \(styles)
        Text: \(text)
        continueClipId: \(String(describing: continueClipId))
        continueAt: \(String(describing: continueAt))
        ------
        """
    }
}

extension Prompt {
    func asGenAPI(model: String, lyricsModel: String? = nil) -> GenParamsSpec {
        let metadata = GenParamsSpec.makeMetadata(
            createMode: createMode,
            lyricsModel: lyricsModel ?? self.lyricsModel, // Override with method lyrics model if available
            weirdnessConstraint: weirdnessConstraint,
            styleWeight: styleWeight,
            audioWeight: audioWeight
        )
        return GenParamsSpec(
            continueAt: continueAt,
            continueClipId: continueClipId,
            coverClipId: coverClipId,
            generationType: .init(rawValue: generationType.rawValue),
            gptDescriptionPrompt: text,
            includeHistory: includeHistory,
            isRemix: isRemix,
            makeInstrumental: instrumental,
            metadata: metadata,
            mv: model,
            prompt: instrumental ? "" : lyrics,
            tags: styles,
            task: .init(rawValue: task?.rawValue ?? ""),
            title: title,
            token: token
        )
    }
}
