import Localization
import SwiftUI

public enum Toast {
    public enum Style: Equatable {
        case warning
        case success
        case information
        case snooze
        case connected
        case disconnected
        case generationComplete
        case preparing
        case caption
        case successV2(RemoteImage?)
        case warningV2(RemoteImage?)

        var image: Image? {
            switch self {
            case .warning, .disconnected: return Image.Icon.warning
            case .success, .connected: return Image.Icon.circleCheck
            case .information: return Image.Icon.alert
            case .snooze: return Image.Icon.timerSnooze
            case .caption: return Image.Icon.caption
            case .preparing, .generationComplete, .successV2, .warningV2:
                return nil
            }
        }
    }

    public enum Message: Equatable {
        case string(String)
        case attributedString(AttributedString)
    }

    public enum Position: Equatable {
        case top
        case bottom
    }

    public struct UndoButton: View {
        private let action: () -> Void

        public init(action: @escaping () -> Void) {
            self.action = action
        }

        public var body: some View {
            Button(L10n.FeatureToasts.undo) {
                UIImpactFeedbackGenerator(style: .light).impactOccurred()
                action()
            }
            .typographyV1(.button1)
            .foregroundStyle(Color.SemanticV1.textLink)
        }
    }

    public struct DismissButton: View {
        private let action: () -> Void

        public init(action: @escaping () -> Void) {
            self.action = action
        }

        public var body: some View {
            Button(L10n.FeatureToasts.dismiss) {
                UIImpactFeedbackGenerator(style: .light).impactOccurred()
                action()
            }
            .typographyV1(.button1)
            .foregroundStyle(Color.SemanticV1.textLink)
        }
    }

    public struct ComponentView<TrailingView: View>: View {
        private var title: String?
        private var message: Message
        private var badge: String?
        private var style: Style
        private var position: Position
        private let displayAsButton: Bool
        private let tapAction: (() -> Void)?
        private let trailingView: TrailingView?

        @Environment(\.colorScheme) private var colorScheme

        public init(
            title: String?,
            message: Message,
            badge: String? = nil,
            style: Style = .information,
            position: Position,
            displayAsButton: Bool = true,
            tapAction: (() -> Void)? = nil,
            trailingView: TrailingView? = nil
        ) {
            self.title = title
            self.message = message
            self.badge = badge
            self.style = style
            self.position = position
            self.displayAsButton = displayAsButton
            self.tapAction = tapAction
            self.trailingView = trailingView
        }

        public var body: some View {
            if let tapAction, displayAsButton {
                Button(action: tapAction) {
                    labelView
                }
                .buttonStyle(.plain)
            } else {
                labelView
                    .onTapGesture { tapAction?() }
            }
        }

        private var labelView: some View {
            HStack(alignment: .center, spacing: 16) {
                iconView

                messageView

                if let trailingView {
                    trailingView
                }
            }
            .colorScheme(colorScheme == .dark ? .light : .dark)
            .padding(16)
            .frame(maxWidth: .infinity)
            .background { background }
        }

        @ViewBuilder
        private var iconView: some View {
            switch style {
            case .generationComplete:
                EmptyView()
                    .frame(width: 24, height: 24)

            case .preparing:
                ProgressView()
                    .progressViewStyle(.circular)
                    .tint(Color.SemanticV1.iconPrimary)
                    .frame(width: 24, height: 24)

            case .successV2(let image):
                if let image {
                    ZStack(alignment: .topTrailing) {
                        image
                            .drawingGroup()
                            .clipShape(.rect(cornerRadius: 8))
                            .frame(width: 40, height: 40)

                        ZStack {
                            Circle()
                                .foregroundStyle(Color.white)
                                .frame(width: 16, height: 16)
                            Image.Icon.success
                                .renderingMode(.template)
                                .resizable()
                                .frame(width: 15, height: 15)
                                .foregroundStyle(Color.SemanticV2.accentPink)
                        }
                        .offset(x: 5, y: -5)
                    }
                    .frame(width: 24, height: 24)
                }

            case .warningV2(let image):
                if let image {
                    ZStack(alignment: .topTrailing) {
                        image
                            .clipShape(.rect(cornerRadius: 8))
                            .frame(width: 40, height: 40)

                        ZStack {
                            Circle()
                                .foregroundStyle(Color.white)
                                .frame(width: 16, height: 16)
                            Image.Icon.success
                                .renderingMode(.template)
                                .resizable()
                                .frame(width: 15, height: 15)
                                .foregroundStyle(Color.SemanticV2.accentPink)
                        }
                        .offset(x: 5, y: -5)
                    }
                    .frame(width: 24, height: 24)
                }

            default:
                if let image = style.image {
                    image
                        .frame(width: 24, height: 24)
                        .foregroundStyle(Color.SemanticV1.iconPrimary)
                }
            }
        }

        private var messageView: some View {
            VStack(alignment: .leading, spacing: 0) {
                HStack {
                    if let title = title, !title.isEmpty {
                        Text(title)
                            .foregroundStyle(Color.SemanticV1.textPrimary)
                            .typographyV1(.body1)
                    }
                    if let badge = badge?.ifNotEmpty {
                        makeBadge(badge)
                    }
                }

                Group {
                    switch message {
                    case .string(let string):
                        if !string.isEmpty {
                            Text(string)
                                .fixedSize(horizontal: false, vertical: true)
                        }

                    case .attributedString(let attributedString):
                        Text(attributedString)
                            .fixedSize(horizontal: false, vertical: true)
                    }
                }
                .foregroundStyle(title == nil ? Color.SemanticV1.textPrimary : Color.SemanticV1.textSecondary)
                .typographyV1(.body2)
            }
            .frame(maxWidth: .infinity, alignment: .leading)
            .multilineTextAlignment(.leading)
            .lineLimit(3)
        }

        private var background: some View {
            RoundedRectangle(cornerRadius: 16)
                .background(
                    RoundedRectangle(cornerRadius: 16)
                        .fill(Color.SemanticV1.backgroundPrimary)
                )
                .modifier(if: position == .top) { view in
                    view
                        .shadow(color: .black.opacity(0.29), radius: 7.5, x: 0, y: 7)
                        .shadow(color: .black.opacity(0.26), radius: 13.5, x: 0, y: 27)
                        .shadow(color: .black.opacity(0.15), radius: 18, x: 0, y: 60)
                }
        }

        @ViewBuilder
        private func makeBadge(_ badgeLabel: String) -> some View {
            Text(badgeLabel)
                .padding(.vertical, 4)
                .padding(.horizontal, 6)
                .typographyV1(.caption6)
                .foregroundColor(.white)
                .background(
                    LinearGradient(
                        colors: [Color(#colorLiteral(red: 0.9888138175, green: 0.4946784973, blue: 0.5179576278, alpha: 1)), Color(#colorLiteral(red: 0.8296493888, green: 0.4170063734, blue: 0.6274469495, alpha: 1)), Color(#colorLiteral(red: 0.4982236028, green: 0.2105129063, blue: 0.632737875, alpha: 1))],
                        startPoint: .init(x: 0, y: 0),
                        endPoint: .init(x: 1, y: 1)
                    )
                )
                .clipShape(.rect(cornerRadius: 4))
        }
    }
}

public extension Toast.ComponentView where TrailingView == AnyView {
    init(title: String?, message: Toast.Message, style: Toast.Style = .information, position: Toast.Position, tapAction: (() -> Void)? = nil) {
        self.init(title: title, message: message, style: style, position: position, tapAction: tapAction, trailingView: nil)
    }
}

struct Toast_Previews: PreviewProvider {
    static var previews: some View {
        VStack(spacing: 24) {
            Toast.ComponentView(
                title: "Your song has been flagged",
                message: .string("This song has been flagged for review"),
                style: .warning,
                position: .top,
                tapAction: {}
            )

            Toast.ComponentView(
                title: "This is a success!",
                message: .string("Celebratory text to go here!"),
                style: .success,
                position: .top,
                tapAction: {}
            )

            Toast.ComponentView(
                title: "Psst, I got some info for ya",
                message: .string("This is text for an info toast."),
                style: .information,
                position: .top,
                tapAction: {}
            )

            Toast.ComponentView(
                title: "This is a success!",
                message: .string("This action is undoable"),
                style: .success,
                position: .bottom,
                trailingView: Toast.UndoButton(action: {})
            )

            Toast.ComponentView(
                title: nil,
                message: .string("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras consectetur blandit quam, sit amet euismod mi condimentum at. Donec eleifend, eros at fringilla commodo, massa dolor dapibus mauris, quis suscipit ligula ante nec sapien. Aenean suscipit tortor ut magna egestas, in eleifend nulla elementum."),
                style: .snooze,
                position: .bottom,
                tapAction: {}
            )

            Toast.ComponentView(
                title: L10n.FeatureCaptions.addCaptionToastTitle,
                message: .string(""),
                style: .caption,
                position: .top,
                tapAction: {},
                trailingView: Text(L10n.FeatureCaptions.addCaptionToastLink)
                    .typographyV1(.caption5)
                    .foregroundStyle(Color.SemanticV1.textLink)
            )

            Toast.ComponentView(
                title: L10n.FeatureCaptions.addCaption,
                message: .string(L10n.FeatureCaptions.addCaptionToastTitle),
                badge: L10n.FeatureCaptions.new,
                style: .caption,
                position: .bottom,
                tapAction: {},
                trailingView: Image(systemName: "chevron.forward")
                    .typographyV1(.body1)
                    .fontWeight(.semibold)
            )
        }
        .padding()
    }
}
