import Localization
import Popovers
import SunoModelClient
import SwiftUI

public protocol ModelSelectorOption {
    var id: String { get }
    var name: String { get }
    var description: String? { get }
    var isPro: Bool { get }
    var isNew: Bool { get }
    var isBeta: Bool { get }
    var songsLeft: Int? { get }
    var canUse: Bool? { get }
    var badges: [ModelBadge]? { get }
}

/// Create V1 Styling
public struct ModelSelector<Model: ModelSelectorOption>: View {
    let selectedModel: Model
    let models: [Model]
    let setSelectedModel: (Model) -> Void
    let infoButtonTapAction: () -> Void
    let showInfoButton: Bool

    public init(
        selectedModel: Model,
        models: [Model],
        setSelectedModel: @escaping (Model) -> Void,
        infoButtonTapAction: @escaping () -> Void,
        showInfoButton: Bool
    ) {
        self.selectedModel = selectedModel
        self.models = models
        self.setSelectedModel = setSelectedModel
        self.infoButtonTapAction = infoButtonTapAction
        self.showInfoButton = showInfoButton
    }

    @State private var isMenuPresented: Bool = false

    public var body: some View {
        HStack(spacing: 3) {
            modelPill
            if showInfoButton {
                infoButton
            }
        }
        .frame(maxWidth: 120, alignment: .leading)
    }

    private var modelPill: some View {
        Menu {
            ForEach(models, id: \.id) { model in
                Button {
                    if model.id != selectedModel.id {
                        setSelectedModel(model)
                    }
                } label: {
                    if model.id == selectedModel.id {
                        Image(systemName: "checkmark")
                    }
                    Text(model.name)
                    if let description = model.description {
                        Text(description)
                    }
                }
            }
        } label: {
            HStack(spacing: 6) {
                Text(selectedModel.name)
                    .typographyV1(.body1.lineHeight(20).size { _ in 14.0 })
                    .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                    .lineLimit(1)

                Image.Icon.chevronDown
                    .resizable()
                    .frame(width: 16, height: 16)
                    .rotationEffect(Angle(degrees: -90))
                    .foregroundStyle(Color.SemanticV2.foregroundPrimary)
            }
            .padding(8)
            .background(RoundedRectangle(cornerRadius: 100).strokeBorder(Color.SemanticV2.foregroundTertiary, lineWidth: 1))
        }
        .contentShape(.rect)
        .onTapGesture {
            isMenuPresented.toggle()
        }
    }

    @ViewBuilder
    var infoButton: some View {
        Button {
            infoButtonTapAction()
        } label: {
            Image.Icon.informationThick
                .resizable()
                .frame(width: 24, height: 24)
                .foregroundColor(Color.SemanticV2.foregroundSecondary)
        }
    }
}

/// Blended Create Styling
public struct ModelSelectorV2<Model: ModelSelectorOption>: View {
    let selectedModel: Model
    let models: [Model]
    let setSelectedModel: (Model) -> Void

    public init(
        selectedModel: Model,
        models: [Model],
        setSelectedModel: @escaping (Model) -> Void
    ) {
        self.selectedModel = selectedModel
        self.models = models
        self.setSelectedModel = setSelectedModel
    }

    public var body: some View {
        Templates.Menu(
            configuration: {
                $0.width = 240
                $0.originAnchor = .top
                $0.popoverAnchor = .bottom
            }
        ) {
            /// We actually need this scroll view as the `Popovers` library can't index into `ForEach` views at the top level
            ScrollView {
                ForEach(models, id: \.id) { model in
                    Button {
                        UIImpactFeedbackGenerator(style: .light).impactOccurred()
                        setSelectedModel(model)
                    } label: {
                        HStack {
                            VStack(alignment: .leading, spacing: 0) {
                                titleRow(model)

                                subtitleRow(model)
                            }

                            Spacer()

                            /// Checkmark
                            if model.id == selectedModel.id {
                                Image(systemName: "checkmark")
                                    .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                            }
                        }
                        .padding(.horizontal, 15)
                        .padding(.vertical, 10)
                    }
                    .opacity(model.canUse == false ? 0.5 : 1)
                }
            }
        } label: { fade in
            Text(selectedModel.name)
                .typographyV1(.subtitleMedium)
                .foregroundStyle(Color.SemanticV2.backgroundPrimary)
                .lineLimit(1)
                .frame(height: 40)
                .frame(minWidth: 60)
                .padding(.horizontal, 8)
                .background {
                    Color.SemanticV2.foregroundPrimary
                }
                .clipShape(.capsule)
                .opacity(fade ? 0.5 : 1)
                .simultaneousGesture(
                    TapGesture()
                        .onEnded {
                            UIImpactFeedbackGenerator(style: .light).impactOccurred()
                        }
                )
        }
    }

    @ViewBuilder
    private func titleRow(_ model: Model) -> some View {
        HStack(alignment: .center, spacing: 4) {
            Text(model.name)
                .typographyV1(.subtitleMedium)
                .foregroundStyle(model.isNew ? Color.SemanticV2.accentBrand : Color.SemanticV2.foregroundPrimary) // Accent if new
                .padding(.trailing, 2)

            if let badges = model.badges {
                ForEach(badges, id: \.self) { badge in
                    switch badge {
                    case .pro:
                        Text(L10n.ComponentLibrary.pro)
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .strokeBorder(Color.SemanticV2.foregroundPrimary, lineWidth: 0.8)
                            }

                    case .beta:
                        Text(L10n.ComponentLibrary.beta)
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                            }

                    case .other(let badgeText):
                        Text(badgeText) // TODO: (JY) Figure out how to localise this. This likely has to wait until backend can localize it for us.
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                            }
                    }
                }
            } else { // Legacy - Doesn't need to be gated as the logic is handled by the model serialization
                // TODO: (JY) Remove once stable

                if model.isBeta {
                    Text(L10n.ComponentLibrary.beta)
                        .typographyV1(.modelTag)
                        .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                        .padding(.horizontal, 4)
                        .padding(.vertical, 1)
                        .background {
                            RoundedRectangle(cornerRadius: 3.18)
                                .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                        }
                }

                if model.isPro {
                    Text(L10n.ComponentLibrary.pro)
                        .typographyV1(.modelTag)
                        .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                        .padding(.horizontal, 4)
                        .padding(.vertical, 1)
                        .background {
                            RoundedRectangle(cornerRadius: 3.18)
                                .stroke(Color.SemanticV2.foregroundPrimary, lineWidth: 0.8)
                        }
                }
            }

            if let songsLeft = model.songsLeft {
                Text(L10n.ComponentLibrary.songsLeft(songsLeft))
                    .typographyV1(.modelTag)
                    .foregroundStyle(Color.SemanticV2.foregroundTertiary)
            }
        }
    }

    @ViewBuilder
    private func subtitleRow(_ model: Model) -> some View {
        if let description = model.description {
            Text(description)
                .typographyV1(.bodySmallNew)
                .foregroundStyle(Color.SemanticV2.foregroundSecondary)
                .multilineTextAlignment(.leading)
        }
    }
}

// TODO: (JY) This is temporary. I feel like there's a better way to create this as a generic component. Currently looks awful on Staging due to the model count.
/// Create V2 Styling
public struct ModelSelectorV3<Model: ModelSelectorOption>: View {
    let selectedModel: Model
    let models: [Model]
    let setSelectedModel: (Model) -> Void

    public init(
        selectedModel: Model,
        models: [Model],
        setSelectedModel: @escaping (Model) -> Void
    ) {
        self.selectedModel = selectedModel
        self.models = models
        self.setSelectedModel = setSelectedModel
    }

    public var body: some View {
        Templates.Menu(
            configuration: {
                $0.width = 240
                $0.originAnchor = .bottom
                $0.popoverAnchor = .top
            }
        ) {
            /// We actually need this scroll view as the `Popovers` library can't index into `ForEach` views at the top level
            ScrollView {
                ForEach(models, id: \.id) { model in
                    Button {
                        UIImpactFeedbackGenerator(style: .light).impactOccurred()
                        setSelectedModel(model)
                    } label: {
                        HStack {
                            VStack(alignment: .leading, spacing: 0) {
                                titleRow(model)

                                subtitleRow(model)
                            }

                            Spacer()

                            /// Checkmark
                            if model.id == selectedModel.id {
                                Image(systemName: "checkmark")
                                    .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                            }
                        }
                        .padding(.horizontal, 15)
                        .padding(.vertical, 10)
                    }
                    .opacity(model.canUse == false ? 0.5 : 1)
                }
            }
        } label: { fade in
            HStack(spacing: 4) {
                Text(selectedModel.name)
                    .figmaMCPTypography(TypographyV1.FigmaMCP.xSmallTitle)
                    .foregroundColor(Color.FigmaMCP.Semantic.foregroundPrimary)
                    .tracking(0.24)

                Image.FigmaMCP.triangleDown
                    .figmaMCPIconStyle(size: 16, semanticColor: Color.FigmaMCP.Semantic.foregroundPrimary)
            }
            .frame(height: 40)
            .padding(.horizontal, 16)
            .background(
                RoundedRectangle(cornerRadius: 100)
                    .stroke(Color.white.opacity(0.1), lineWidth: 1)
            )
            .opacity(fade ? 0.5 : 1)
            .simultaneousGesture(
                TapGesture()
                    .onEnded {
                        UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    }
            )
        }
    }

    @ViewBuilder
    private func titleRow(_ model: Model) -> some View {
        HStack(alignment: .center, spacing: 4) {
            Text(model.name)
                .typographyV1(.subtitleMedium)
                .foregroundStyle(model.isNew ? Color.SemanticV2.accentBrand : Color.SemanticV2.foregroundPrimary) // Accent if new
                .padding(.trailing, 2)

            if let badges = model.badges {
                ForEach(badges, id: \.self) { badge in
                    switch badge {
                    case .pro:
                        Text(L10n.ComponentLibrary.pro)
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .strokeBorder(Color.SemanticV2.foregroundPrimary, lineWidth: 0.8)
                            }

                    case .beta:
                        Text(L10n.ComponentLibrary.beta)
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                            }

                    case .other(let badgeText):
                        Text(badgeText) // TODO: (JY) Figure out how to localise this. This likely has to wait until backend can localize it for us.
                            .typographyV1(.modelTag)
                            .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                            .padding(.horizontal, 4)
                            .padding(.vertical, 1)
                            .background {
                                RoundedRectangle(cornerRadius: 3.18)
                                    .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                            }
                    }
                }
            } else { // Legacy - Doesn't need to be gated as the logic is handled by the model serialization
                // TODO: (JY) Remove once stable

                if model.isBeta {
                    Text(L10n.ComponentLibrary.beta)
                        .typographyV1(.modelTag)
                        .foregroundStyle(Color.SemanticV1.textOnDark) // V2 doesn't have prominent `onDark` colors yet
                        .padding(.horizontal, 4)
                        .padding(.vertical, 1)
                        .background {
                            RoundedRectangle(cornerRadius: 3.18)
                                .fill(Color.SemanticV2.foregroundSecondaryOnLight)
                        }
                }

                if model.isPro {
                    Text(L10n.ComponentLibrary.pro)
                        .typographyV1(.modelTag)
                        .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                        .padding(.horizontal, 4)
                        .padding(.vertical, 1)
                        .background {
                            RoundedRectangle(cornerRadius: 3.18)
                                .stroke(Color.SemanticV2.foregroundPrimary, lineWidth: 0.8)
                        }
                }
            }

            if let songsLeft = model.songsLeft {
                Text(L10n.ComponentLibrary.songsLeft(songsLeft))
                    .typographyV1(.modelTag)
                    .foregroundStyle(Color.SemanticV2.foregroundTertiary)
            }
        }
    }

    @ViewBuilder
    private func subtitleRow(_ model: Model) -> some View {
        if let description = model.description {
            Text(description)
                .typographyV1(.bodySmallNew)
                .foregroundStyle(Color.SemanticV2.foregroundSecondary)
                .multilineTextAlignment(.leading)
        }
    }
}

// Specialized model selectors
public typealias MusicModelSelector = ModelSelector<SunoModelMetaData>

public typealias MusicModelSelectorV2 = ModelSelectorV2<SunoModelMetaData>

public typealias LyricsModelSelectorV2 = ModelSelectorV2<LyricsModelMetadata>

extension SunoModelMetaData: ModelSelectorOption {}

extension LyricsModelMetadata: ModelSelectorOption {
    public var isPro: Bool { false }
    public var isNew: Bool { false }
    public var isBeta: Bool { false }
    public var songsLeft: Int? { nil }
    public var canUse: Bool? { nil }
    public var badges: [ModelBadge]? { nil }
}

extension TypographyV1 {
    /// size: 9.55pt, lineHeight: 12.74pt, weight: medium
    static let modelTag: TypographyV1 = .init(
        name: "modelTag",
        size: 9.55,
        style: .body,
        weight: .ppNeueMontrealMedium,
        lineHeight: 12.74
    )
}
