import APIClient
import Localization
import SwiftUI

/*
 Reusable Pill component used to display:
 - Versioning information (e.g., "v4", "v4.5+", "Upgrade to v4.5+", etc.)
 - Custom labels (e.g., "Cover", "Edit", "Extension", "Upload")
 - Model preview pills ("Model preview v4", loading, etc.)
 - Model badge pills (BE-driven, with styled background/border)

 Seen in ClipListItem (Library, Playlists, Genre Playlists, etc.),
 OmniPlayer, Song Actions, and More Info.
 */
public struct Pill: View {
    @Environment(\.colorScheme) var colorScheme

    public enum PillType {
        // Brand pink "v4.5+" and regular "v4" pills,
        // as well as pink upsell pills.
        case versioning(version: VersionDisplayState)

        // "Cover", "Edit", "Extension", and "Upload" pills on Song Rows.
        case custom(label: String)

        // Pink solid background variant used for previewing/unlocking model versions.
        case modelPreview(version: VersionDisplayState)

        // BE-driven model badge with custom colors and border.
        case modelBadge(style: ModelBadgeStyle)

        // Used to display "v4", "v4.5+" etc. on ClipListItem (Song Rows),
        // OmniPlayer, Song Actions, and More Info. Also controls upsell states.
        // showing the "Upgrade to %" upsell when necessary.
        public enum VersionDisplayState: Equatable {
            case loading
            case v3_5
            case v4
            case v4_5
            case v4_5Plus
            case upsellV4_5Plus
            case upsellV5
            case v5
        }
    }

    let type: PillType
    let action: (() -> Void)?

    public init(
        type: PillType,
        action: (() -> Void)? = nil
    ) {
        self.type = type
        self.action = action
    }

    public var body: some View {
        if let action {
            Button(action: action) {
                content
            }
            .buttonStyle(.plain)
        } else {
            content
        }
    }

    @ViewBuilder
    private var content: some View {
        switch type {
        case .custom(let label):
            defaultPill(label: label)

        case .versioning(let version):
            versionPill(for: version)

        case .modelPreview(let version):
            modelPreviewPill(for: version)

        case .modelBadge(let style):
            modelBadgePill(style: style)
        }
    }

    // MARK: - Variants

    @ViewBuilder
    private func defaultPill(label: String) -> some View {
        Text(label)
            .typographyV1(.caption4.neueMontrealRegular())
            .padding(.horizontal, Constants.regularHorizontalPadding)
            .padding(.vertical, Constants.verticalPaddingForVersionLabel)
            .foregroundColor(Color.SemanticV2.foregroundTertiary)
            .lineLimit(1)
            .minimumScaleFactor(0.8)
            .background(
                RoundedRectangle(cornerRadius: Constants.pillCornerRadius)
                    .fill(Color.SemanticV2.backgroundGlassThick)
            )
    }

    @ViewBuilder
    private func versionPill(for state: PillType.VersionDisplayState) -> some View {
        ZStack {
            switch state {
            case .loading:
                Color.clear
                    .overlay {
                        ProgressView()
                            .progressViewStyle(.circular)
                    }

            case .v3_5, .v4, .v4_5, .v4_5Plus, .v5, .upsellV4_5Plus, .upsellV5:
                Text(primaryPillText)
                    .typographyV1(.caption4.neueMontrealRegular())
                    .padding(.horizontal, horizontalPadding)
                    .padding(.vertical, verticalPadding)
                    .foregroundColor(textColor)
                    .background(backgroundView)
            }
        }
    }

    @ViewBuilder
    private func modelPreviewPill(for state: PillType.VersionDisplayState) -> some View {
        ZStack {
            switch state {
            case .loading:
                HStack(spacing: 6) {
                    GradientSpinner(size: .small)
                    Text(L10n.FeatureClipList.unlockingSong)
                        .typographyV1(.caption4.neueMontrealRegular())
                }
                .padding(.horizontal, Constants.regularHorizontalPadding)
                .padding(.vertical, Constants.verticalPaddingForVersionLabel)
                .foregroundColor(textColor)
                .background(backgroundView)

            default:
                Text(L10n.FeatureClipList.modelPreview(primaryPillText))
                    .typographyV1(.caption4.neueMontrealRegular())
                    .padding(.horizontal, Constants.regularHorizontalPadding)
                    .padding(.vertical, Constants.verticalPaddingForVersionLabel)
                    .foregroundColor(textColor)
                    .background(backgroundView)
            }
        }
    }

    @ViewBuilder
    private func modelBadgePill(style: ModelBadgeStyle) -> some View {
        let colorStyle = colorScheme == .light ? style.light : style.dark

        Text(style.displayName)
            .typographyV1(.caption4.neueMontrealRegular())
            .padding(.horizontal, Constants.regularHorizontalPadding)
            .padding(.vertical, Constants.verticalPaddingForVersionLabel)
            .foregroundColor(Color(hex: colorStyle.textColor))
            .lineLimit(1)
            .background(
                RoundedRectangle(cornerRadius: Constants.pillCornerRadius)
                    .fill(Color(hex: colorStyle.backgroundColor))
                    .overlay(
                        RoundedRectangle(cornerRadius: Constants.pillCornerRadius)
                            .strokeBorder(Color(hex: colorStyle.borderColor), lineWidth: 1)
                    )
            )
    }
}

private extension Pill {
    enum Constants {
        static let regularHorizontalPadding: CGFloat = 8.0
        static let smallHorizontalPadding: CGFloat = 6.0
        static let verticalPaddingForVersionLabel: CGFloat = 2.0
        static let verticalPaddingForUpgradeUpsell: CGFloat = 2.5
        static let pillCornerRadius: CGFloat = 34.0

        enum Versioning {
            static let v4Copy: String = "v4"
            static let v3Dot5Copy: String = "v3.5"
            static let v4Dot5Copy: String = "v4.5"
            static let v4Dot5PlusCopy: String = "v4.5+"
            static let v5Copy: String = "v5"
            static let pinkAccentColor: Color = Color.PaletteV2.pink600

            static var upgradeToV4Dot5Copy: String {
                "\(L10n.FeatureBrandedAlert.upgradeTo(v4Dot5Copy))"
            }

            static var upgradeToV5Copy: String {
                "\(L10n.FeatureBrandedAlert.upgradeTo(v5Copy))"
            }
        }
    }

    // MARK: - Styling helpers

    var textColor: Color {
        switch type {
        case .versioning(let state):
            switch state {
            case .loading: return .clear
            case .v3_5, .v4: return Color.SemanticV2.foregroundTertiary
            case .v4_5, .v4_5Plus, .v5, .upsellV4_5Plus, .upsellV5:
                return Constants.Versioning.pinkAccentColor
            }

        case .modelPreview(let state):
            switch state {
            case .loading: return .primary
            case .v3_5, .v4: return Color.SemanticV2.foregroundTertiary
            case .v4_5, .v4_5Plus, .v5, .upsellV4_5Plus, .upsellV5:
                return .white
            }

        case .custom, .modelBadge:
            return Color.SemanticV2.foregroundTertiary
        }
    }

    @ViewBuilder
    var backgroundView: some View {
        // Versioning/modelPreview share the same container shape,
        // with different color/opacity rules.
        switch type {
        case .versioning, .modelPreview:
            backgroundColor
                .background(.ultraThinMaterial)
                .clipShape(.rect(cornerRadius: Constants.pillCornerRadius))

        case .custom, .modelBadge:
            // These variants draw their own backgrounds.
            EmptyView()
        }
    }

    var backgroundColor: Color {
        switch type {
        case .versioning(let state), .modelPreview(let state):
            var color: Color
            switch state {
            case .loading:
                color = .clear
            case .v3_5:
                color = .clear
            case .v4:
                color = Color.SemanticV2.backgroundGlassThick
            case .v4_5, .v4_5Plus, .v5, .upsellV4_5Plus, .upsellV5:
                color = Constants.Versioning.pinkAccentColor
            }

            // Versioning uses light pink background, modelPreview uses solid pink.
            if case .versioning = type {
                color = color.opacity(0.10)
            }
            return color

        case .custom, .modelBadge:
            return Color.SemanticV2.backgroundGlassThick
        }
    }

    var primaryPillText: String {
        switch type {
        case .custom(let label):
            return label

        case .versioning(let state), .modelPreview(let state):
            switch state {
            case .loading, .v3_5:
                return ""
            case .v4:
                return Constants.Versioning.v4Copy
            case .v4_5:
                return Constants.Versioning.v4Dot5Copy
            case .v4_5Plus:
                return Constants.Versioning.v4Dot5PlusCopy
            case .v5:
                return Constants.Versioning.v5Copy
            case .upsellV4_5Plus:
                return Constants.Versioning.upgradeToV4Dot5Copy
            case .upsellV5:
                return Constants.Versioning.upgradeToV5Copy
            }

        case .modelBadge:
            return ""
        }
    }

    var verticalPadding: CGFloat {
        // Only versioning pills use the upsell-specific vertical padding.
        if case .versioning(let state) = type,
           state == .upsellV4_5Plus || state == .upsellV5
        {
            return Constants.verticalPaddingForUpgradeUpsell
        }
        return Constants.verticalPaddingForVersionLabel
    }

    var horizontalPadding: CGFloat {
        // Only versioning pills use the upsell-specific horizontal padding.
        if case .versioning(let state) = type,
           state == .upsellV4_5Plus || state == .upsellV5
        {
            return Constants.smallHorizontalPadding
        }
        return Constants.regularHorizontalPadding
    }
}

// MARK: - Mapping convenience

public extension Pill.PillType.VersionDisplayState {
    init(from model: Clip.HighLevelModelVersion) {
        switch model {
        case .previousToV4:
            self = .v3_5
        case .v4:
            self = .v4
        case .v4_5:
            self = .v4_5
        case .v4_5Plus:
            self = .v4_5Plus
        case .v5:
            self = .v5
        }
    }
}
