import Foundation
import Lottie
import SwiftUI

// MARK: - ButtonType

public enum ButtonType {
    case primary
    case secondary
    case tonal
    case outline
    case danger
    case text
    case disabled
}

// MARK: - ButtonSize

public enum ButtonSize {
    case large
    case medium
    case small
}

public struct ButtonStyleData {
    let background: Color
    let content: Color
    let border: Color?
    let borderWidth: CGFloat
}

public extension ButtonType {
    func backgroundColor(loading: Bool) -> Color {
        switch self {
        case .disabled: return Colors.black05.opacity(loading ? 0.75 : 1)
        case .primary: return Colors.primary.opacity(loading ? 0.75 : 1)
        case .secondary: return Colors.card.opacity(loading ? 0.75 : 1)
        case .outline: return Colors.card.opacity(loading ? 0.75 : 1)
        case .tonal: return Colors.primaryLight.opacity(loading ? 0.75 : 1)
        case .danger: return Colors.error.opacity(loading ? 0.75 : 1)
        case .text: return .clear
        }
    }

    func contentColor(loading: Bool) -> Color {
        switch self {
        case .disabled: return Colors.black06.opacity(loading ? 0.75 : 1)
        case .primary: return Colors.black01
        case .secondary: return Colors.black17
        case .outline: return Colors.primary
        case .tonal: return Colors.primary
        case .danger: return Colors.black01
        case .text: return Colors.primary
        }
    }

    var borderColor: Color? {
        switch self {
        case .outline: return Colors.primary
        case .secondary: return Colors.black04
        default: return nil
        }
    }

    var borderWidth: CGFloat {
        switch self {
        case .outline: return 1
        case .secondary: return 1
        default: return 0
        }
    }
}

extension ButtonSize {
    var height: CGFloat {
        switch self {
        case .large: return 48
        case .medium: return 36
        case .small: return 28
        }
    }

    var padding: CGFloat {
        switch self {
        case .large: return Spacing.L
        case .medium: return Spacing.M
        case .small: return Spacing.S
        }
    }

    var radius: CGFloat {
        Radius.S
    }

    var iconSize: CGFloat {
        switch self {
        case .large: return 24
        case .medium, .small: return 16
        }
    }

    var iconSpacing: CGFloat {
        switch self {
        case .small: return Spacing.XS
        case .medium, .large: return Spacing.S
        }
    }

    var typographyStyle: TypographyStyle {
        switch self {
        case .large: return .actionDefaultBold
        case .medium: return .actionSBold
        case .small: return .actionXsBold
        default : return .actionDefaultBold
        }
    }
}

public struct Button: View {
    var title: String
    var action: () -> Void
    var type: ButtonType
    var size: ButtonSize
    var iconLeft: AnyView?
    var iconRight: AnyView?
    var isFull: Bool
    var loading: Bool

    public init(
        title: String = "",
        action: @escaping () -> Void,
        type: ButtonType = .primary,
        size: ButtonSize = .large,
        iconLeft: AnyView? = nil,
        iconRight: AnyView? = nil,
        isFull: Bool = true,
        loading: Bool = false
    ) {
        self.title = title
        self.action = action
        self.type = type
        self.size = size
        self.iconLeft = iconLeft
        self.iconRight = iconRight
        self.isFull = isFull
        self.loading = loading
    }

    private func shouldLoadingOnLeft(_ left: AnyView?, _ right: AnyView?) -> Bool {
        let hasLeft = left != nil
        let hasRight = right != nil

        switch (hasLeft, hasRight) {
        case (false, false): return true
        case (true, false): return true
        case (false, true): return false
        case (true, true): return false
        }
    }

    public var body: some View {
        let loadingOnLeft = shouldLoadingOnLeft(iconLeft, iconRight)

        let bg = type.backgroundColor(loading: loading)
        let fg = type.contentColor(loading: loading)
        let border = type.borderColor
        let borderWidth = type.borderWidth

        SwiftUI.Button(action: {
            if !loading, type != .disabled {
                action()
            }
        }) {
            HStack(spacing: size.iconSpacing) {
                if loading && loadingOnLeft {
                    LottieView(name: "lottie_circle_loader", loopMode: .loop)
                        .frame(width: size.iconSize, height: size.iconSize)
                        .colorMultiply(fg)

                } else if let iconLeft = iconLeft {
                    iconLeft.frame(width: size.iconSize, height: size.iconSize)
                }

                MomoText(title, typography: size.typographyStyle, color: fg)
                    .lineLimit(1)
                    .truncationMode(.tail)

                if loading && !loadingOnLeft {
                    LottieView(name: "lottie_circle_loader", loopMode: .loop)
                        .frame(width: size.iconSize, height: size.iconSize)
                        .colorMultiply(fg)

                } else if let iconRight = iconRight {
                    iconRight.frame(width: size.iconSize, height: size.iconSize)
                }
            }
            .frame(maxWidth: isFull ? .infinity : nil)
            .padding(.horizontal, size.padding)
            .frame(height: size.height)
            .background(bg)
            .overlay(
                RoundedRectangle(cornerRadius: size.radius)
                    .stroke(border ?? .clear, lineWidth: border != nil ? borderWidth : 0)
            )
            .clipShape(RoundedRectangle(cornerRadius: size.radius))
            .opacity(loading ? 0.75 : 1)
        }
        .disabled(type == .disabled || loading)
    }
}
