import Foundation
import SwiftUI

public struct PrimaryButtonV1<LeadingView: View>: View {
    private let title: String
    private let isLoading: Bool
    private let isIconOnly: Bool
    private let colorCombination: PillButtonStyleV1.ColorCombination
    private let leadingView: LeadingView
    private let action: () -> Void
    private let preferredSize: PillButtonSizeV1?
    private var size: PillButtonSizeV1 { preferredSize ?? environmentSize }

    @Environment(\.pillButtonSizeV1) private var environmentSize
    @Environment(\.isEnabled) private var isEnabled

    public init(
        title: String = "",
        isLoading: Bool = false,
        isIconOnly: Bool = false,
        @ViewBuilder leadingView: () -> LeadingView,
        action: @escaping () -> Void
    ) {
        self.init(
            title: title,
            isLoading: isLoading,
            isIconOnly: isIconOnly,
            colorCombination: primaryButtonV1ColorCombinationDark,
            leadingView: leadingView,
            action: action
        )
    }

    public init(
        title: String = "",
        isLoading: Bool = false,
        isIconOnly: Bool = false,
        colorCombination: PillButtonStyleV1.ColorCombination,
        preferredSize: PillButtonSizeV1? = nil,
        @ViewBuilder leadingView: () -> LeadingView,
        action: @escaping () -> Void
    ) {
        self.title = title
        self.isLoading = isLoading
        self.isIconOnly = isIconOnly
        self.colorCombination = colorCombination
        self.preferredSize = preferredSize
        self.leadingView = leadingView()
        self.action = action
    }

    public var body: some View {
        Button {
            action()
        } label: {
            if isIconOnly {
                leadingView // need a solution here to allow .resizable and .aspectRatio(contentMode: .fit)
                    .frame(width: size.iconWidth, height: size.iconWidth)
            } else {
                HStack {
                    leadingView // need a solution here to allow .resizable and .aspectRatio(contentMode: .fit)
                        .frame(width: size.iconWidth, height: size.iconWidth)

                    Text(title)
                        .accessibilityLabel(title)
                }
            }
        }
        .disabled(isLoading)
        .buttonStyle(
            PillButtonStyleV1(
                isLoading: isLoading,
                isEnabled: isEnabled,
                isIconOnly: isIconOnly,
                size: size,
                colorCombination: colorCombination
            )
        )
        .contentShape(.rect)
    }
}

public extension PrimaryButtonV1 where LeadingView == EmptyView {
    /// Create a primary button without a leading view.
    /// - Parameters:
    ///   - title: Centered title label
    ///   - isLoading: True to display a loading indicator instead of the label.
    ///   - action: Action to be triggered on tap
    init(
        title: String,
        isLoading: Bool = false,
        preferredSize: PillButtonSizeV1? = nil,
        action: @escaping () -> Void
    ) {
        self.init(
            title: title,
            isLoading: isLoading,
            colorCombination: primaryButtonV1ColorCombinationDark,
            preferredSize: preferredSize,
            leadingView: { EmptyView() },
            action: action
        )
    }

    init(
        title: String,
        isLoading: Bool = false,
        colorCombination: PillButtonStyleV1.ColorCombination,
        preferredSize: PillButtonSizeV1? = nil,
        action: @escaping () -> Void
    ) {
        self.init(
            title: title,
            isLoading: isLoading,
            colorCombination: colorCombination,
            preferredSize: preferredSize,
            leadingView: { EmptyView() },
            action: action
        )
    }
}

public let primaryButtonV1ColorCombinationDark = PillButtonStyleV1.ColorCombination(
    enabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.textInvert,
        background: .SemanticV1.backgroundInvert,
        loading: .SemanticV1.iconInvert,
        border: .clear
    ),
    pressed: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.textInvert.opacity(0.5),
        background: .SemanticV1.backgroundInvert,
        loading: .SemanticV1.iconInvert,
        border: .clear
    ),
    disabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.backgroundPrimary,
        background: .SemanticV1.textTertiary,
        loading: .SemanticV1.iconTertiary,
        border: .clear
    )
)

public let primaryButtonV1ColorCombinationSecondary = PillButtonStyleV1.ColorCombination(
    enabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.textPrimary,
        background: .SemanticV1.backgroundSecondary,
        loading: .SemanticV1.iconPrimary,
        border: .clear
    ),
    pressed: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.textPrimary.opacity(0.5),
        background: .SemanticV1.backgroundTertiary,
        loading: .SemanticV1.iconPrimary.opacity(0.5),
        border: .clear
    ),
    disabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.textPrimary.opacity(0.5),
        background: .SemanticV1.backgroundPrimary.opacity(0.5),
        loading: .SemanticV1.iconTertiary.opacity(0.5),
        border: .clear
    )
)

let primaryButtonV1ColorCombinationAlwaysDark = PillButtonStyleV1.ColorCombination(
    enabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.alwaysLighterBeige,
        background: .SemanticV1.alwaysBlack1,
        loading: .SemanticV1.alwaysLighterBeige,
        border: .clear
    ),
    pressed: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.alwaysLighterBeige.opacity(0.5),
        background: .SemanticV1.alwaysBlack1,
        loading: .SemanticV1.alwaysLighterBeige,
        border: .clear
    ),
    disabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.alwaysBlack1,
        background: .SemanticV1.alwaysDarkerBeige,
        loading: .SemanticV1.alwaysDarkerBeige,
        border: .clear
    )
)

let primaryButtonV1ColorCombinationAlwaysLight = PillButtonStyleV1.ColorCombination(
    enabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV2.backgroundPrimary,
        background: .SemanticV2.foregroundPrimary,
        loading: .SemanticV1.alwaysLighterBeige,
        border: .clear
    ),
    pressed: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV2.backgroundPrimary.opacity(0.5),
        background: .SemanticV2.foregroundPrimary,
        loading: .SemanticV1.alwaysLighterBeige,
        border: .clear
    ),
    disabled: PillButtonStyleV1.ColorSet(
        foreground: .SemanticV1.alwaysBlack1,
        background: .SemanticV1.alwaysDarkerBeige,
        loading: .SemanticV1.alwaysDarkerBeige,
        border: .clear
    )
)

public extension PillButtonStyleV1.ColorCombination {
    static var dark: Self { primaryButtonV1ColorCombinationDark }
    static var darkAlways: Self { primaryButtonV1ColorCombinationAlwaysDark }
    static var secondary: Self { primaryButtonV1ColorCombinationSecondary }
    static var lightAlways: Self { primaryButtonV1ColorCombinationAlwaysLight }
}

struct PrimaryButtonV1_Previews: PreviewProvider {
    static var previews: some View {
        PillButtonSampleV1(title: "Button")
    }
}

struct PillButtonSampleV1: View {
    let title: String

    init(title: String) {
        self.title = title
    }

    var body: some View {
        VStack {
            PrimaryButtonV1(
                title: title,
                preferredSize: .medium,
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: false,
                isIconOnly: false,
                colorCombination: primaryButtonV1ColorCombinationDark,
                preferredSize: .medium,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: true,
                isIconOnly: false,
                colorCombination: primaryButtonV1ColorCombinationDark,
                preferredSize: .medium,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: false,
                isIconOnly: true,
                colorCombination: primaryButtonV1ColorCombinationDark,
                preferredSize: .mediumAdaptive,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                preferredSize: .medium,
                action: {}
            )
            .disabled(true)

            PrimaryButtonV1(
                title: title,
                colorCombination: .secondary,
                preferredSize: .medium,
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: false,
                isIconOnly: false,
                colorCombination: .secondary,
                preferredSize: .medium,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: true,
                isIconOnly: false,
                colorCombination: .secondary,
                preferredSize: .medium,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                isLoading: false,
                isIconOnly: true,
                colorCombination: .secondary,
                preferredSize: .mediumAdaptive,
                leadingView: { Image(systemName: "globe") },
                action: {}
            )

            PrimaryButtonV1(
                title: title,
                colorCombination: .secondary,
                preferredSize: .medium,
                action: {}
            )
            .disabled(true)
        }
        .padding()
    }
}
