import ComponentLibrary
import SwiftUI

// This alert isn't created via a `BindingAlert` reducer but instead
// used directly in the view via the `.brandedStackAlert()` modifiers.

public enum BrandedStackAlert {
    public struct Button {
        var label: String
        var action: (() -> Void)?
        var isProminent: Bool = false

        public init(label: String, action: (() -> Void)?, isProminent: Bool = false) {
            self.label = label
            self.action = action
            self.isProminent = isProminent
        }
    }

    public struct Configuration {
        var icon: Image?
        var title: String
        var detail: AttributedString
        var primaryButton: Button?
        var secondaryButton: Button?
        var tertiaryButton: Button?

        public init(
            title: String,
            icon: Image? = nil,
            detail: String,
            primaryButton: Button? = nil,
            secondaryButton: Button? = nil,
            tertiaryButton: Button? = nil
        ) {
            self.icon = icon
            self.title = title
            self.primaryButton = primaryButton
            self.secondaryButton = secondaryButton
            self.tertiaryButton = tertiaryButton

            // Attempt to interpret the detail as markdown
            guard var attributedDetail = try? AttributedString(markdown: detail) else {
                self.detail = AttributedString(detail)
                return
            }

            // Style links to match text color and add underline
            for run in attributedDetail.runs {
                guard run.link != nil else { continue }
                attributedDetail[run.range].foregroundColor = Color.SemanticV2.foregroundPrimary
                attributedDetail[run.range].underlineStyle = .single
            }
            self.detail = attributedDetail
        }

        fileprivate static var empty: Self { Configuration(title: "", detail: "") }
    }
}

private struct _BrandedStackAlert: View {
    var config: BrandedStackAlert.Configuration
    var dismiss: () -> Void

    var body: some View {
        Group {
            VStack(spacing: 8) {
                Group {
                    if let icon = config.icon {
                        icon
                            .frame(width: 24, height: 24)
                    }

                    Text(config.title)
                        .typographyV1(.playlistCards)

                    Text(config.detail)
                        .typographyV1(.body2)
                        .opacity(0.8)
                }
                .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                .multilineTextAlignment(.center)
                .padding(.horizontal, 24)

                VStack(spacing: 8) {
                    if let primaryButton = config.primaryButton {
                        optionButton(primaryButton)
                    }
                    if let secondaryButton = config.secondaryButton {
                        optionButton(secondaryButton)
                    }
                    if let tertiaryButton = config.tertiaryButton {
                        optionButton(tertiaryButton)
                    }
                }
                .padding(.top, 20)
            }
            .padding(.top, 44)
            .padding(.bottom, 10)
            .padding(.horizontal, 20)
            .background(Color.SemanticV2.backgroundSecondary)
            .clipShape(.rect(cornerRadius: 32))
            .padding(.horizontal, 40)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea(.all)
        .background(Color.SemanticV1.alwaysBlack1.opacity(0.75))
    }

    @ViewBuilder
    private func optionButton(_ button: BrandedStackAlert.Button) -> some View {
        let action = button.action ?? dismiss
        Button(button.label, action: action)
            .padding(12)
            .typographyV1(.subtitleMedium)
            .frame(maxWidth: .infinity)
            .foregroundStyle(button.isProminent ? Color.SemanticV2.backgroundPrimary : Color.SemanticV2.foregroundPrimary)
            .background(button.isProminent ? Color.SemanticV2.foregroundPrimary : .clear)
            .clipShape(.capsule)
    }
}

private struct BrandedStackAlertModifier: ViewModifier {
    private let alert: _BrandedStackAlert
    private let isPresenting: () -> Bool

    public init(
        _ config: BrandedStackAlert.Configuration,
        isPresenting: @escaping () -> Bool,
        dismiss: @escaping () -> Void
    ) {
        self.alert = _BrandedStackAlert(config: config, dismiss: dismiss)
        self.isPresenting = isPresenting
    }

    func body(content: Content) -> some View {
        content
            .overlay {
                ZStack {
                    if isPresenting() {
                        Color.SemanticV1.backgroundQuaternary
                            .opacity(0.4)
                            .transition(.opacity.animation(.easeOut))
                            .background(
                                Color.clear
                                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                            )
                            .ignoresSafeArea()
                            .overlay {
                                alert
                            }
                    }
                }
            }
    }
}

public extension View {
    @ViewBuilder
    func brandedStackAlert<T>(
        item: Binding<T?>,
        config: (T) -> BrandedStackAlert.Configuration
    ) -> some View {
        modifier(
            BrandedStackAlertModifier(
                .makeConfig(for: item, using: config),
                isPresenting: { item.wrappedValue != nil },
                dismiss: { item.wrappedValue = nil }
            )
        )
    }

    @ViewBuilder
    func brandedStackAlert(
        isOn: Binding<Bool>,
        config: BrandedStackAlert.Configuration
    ) -> some View {
        modifier(
            BrandedStackAlertModifier(
                config,
                isPresenting: { isOn.wrappedValue == true },
                dismiss: { isOn.wrappedValue = false }
            )
        )
    }
}

private extension BrandedStackAlert.Configuration {
    static func makeConfig<T>(
        for item: Binding<T?>,
        using maker: (T) -> BrandedStackAlert.Configuration
    ) -> BrandedStackAlert.Configuration {
        guard let value = item.wrappedValue else { return .empty }
        return maker(value)
    }
}

extension TypographyV1 {}
