import Adamantium
import CachedAsyncImage
import SwiftUI

// swiftlint:disable file_length

public struct Banner: View {
    @Environment(\.colorScheme) private var colorScheme
    @ObservedObject var auraCoordinator: CircleMaskedAuraView.Coordinator

    public enum Background: Equatable {
        case none
        case auraShader(completionRatio: Double)

        public var isComplete: Bool {
            switch self {
            case .none:
                return false
            case .auraShader(let completionRatio):
                return completionRatio >= 1.0
            }
        }

        public var isJustStarting: Bool {
            switch self {
            case .none:
                return false
            case .auraShader(let completionRatio):
                return completionRatio <= 0.2
            }
        }

        public static func == (lhs: Self, rhs: Self) -> Bool {
            switch lhs {
            case .none:
                guard case .none = rhs else { return false }
                return true

            case .auraShader(let lhsCompletionRatio):
                guard case .auraShader(let rhsCompletionRatio) = rhs else { return false }
                return lhsCompletionRatio == rhsCompletionRatio
            }
        }
    }

    public enum PreviewAncillaryBubbleIcon: Equatable {
        case none
        case instagram
        case download
        case facebook
        case tiktok
    }

    public enum Style: Equatable {
        public enum SubStyle {
            case clipGeneration
            case shareAssetGeneration
        }

        case warning
        case success(SubStyle)
        case information
        case progress(SubStyle)
        case custom(Image)

        var image: Image? {
            switch self {
            case .warning: return Image.Icon.informationThick
            case .success: return Image.Icon.checkV1
            case .information: return Image.Icon.informationThick
            case .progress: return nil
            case .custom(let icon): return icon
            }
        }

        var fillColor: Color {
            switch self {
            case .success: return Color.SemanticV1.textLink
            default: return Color.SemanticV1.backgroundSecondary
            }
        }

        var textAlignment: Alignment {
            switch self {
            case .progress: return .center
            default: return .leading
            }
        }

        var allowDismiss: Bool {
            switch self {
            case .progress: return false
            default: return true
            }
        }
    }

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

    private var message: Message
    private var style: Style
    private var previewCardBubbleIcon: PreviewAncillaryBubbleIcon
    private var background: Background
    private var previewImageURL: URL?
    private let tapAction: (() -> Void)?
    private let dismissAction: () -> Void

    @State var isShowingPreviewCard = false

    public init(
        auraCoordinator: CircleMaskedAuraView.Coordinator,
        message: Message,
        style: Style = .information,
        previewCardBubbleIcon: PreviewAncillaryBubbleIcon = .none,
        background: Background = .none,
        tapAction: (() -> Void)? = nil,
        previewImageURL: URL? = nil,
        dismissAction: @escaping () -> Void
    ) {
        self.auraCoordinator = auraCoordinator
        self.message = message
        self.style = style
        self.previewCardBubbleIcon = previewCardBubbleIcon
        self.background = background
        self.previewImageURL = previewImageURL
        self.tapAction = tapAction
        self.dismissAction = dismissAction
    }

    public var body: some View {
        VStack(spacing: .zero) {
            Button(
                action: { tapAction?() },
                label: {
                    VStack(spacing: 0) {
                        HStack(alignment: .center, spacing: 16) {
                            if let image = style.image {
                                Circle()
                                    .fill(style.fillColor)
                                    .frame(width: 44, height: 44)
                                    .overlay {
                                        image
                                            .resizable()
                                            .frame(width: 24, height: 24, alignment: .center)
                                    }
                                    .background {
                                        if case .custom = style {
                                            InfiniteCircularProgressView(strokeColor: Color.SemanticV1.iconLink)
                                                .padding(2)
                                        }
                                    }
                                    .foregroundStyle(Color.SemanticV1.iconPrimary)
                            }

                            HStack(spacing: .zero) {
                                ancillaryIcon
                                primaryViewText
                                Spacer()
                            }
                            .padding(.leading, 8.0)

                            dismissView
                        }
                        .padding(16)

                        var barOpacity: CGFloat {
                            switch style {
                            case .progress:
                                return 1.0
                            default:
                                return 0.0
                            }
                        }

                        InfiniteProgressBarView()
                            .frame(height: 4.0)
                            .opacity(barOpacity)
                    }
                    .background {
                        Group {
                            switch background {
                            case .none:
                                Color.SemanticV1.backgroundPrimary

                            case .auraShader:
                                ZStack {
                                    auraView
                                        .overlay {
                                            previewCard
                                        }
                                }
                                .ignoresSafeArea()
                            }
                        }
                        .ignoresSafeArea()
                    }
                }
            )
            .environment(\.colorScheme, .dark)
            .buttonStyle(.plain)
            Color.SemanticV1.backgroundPrimary
                .frame(height: 8.0)
                .onAppear {
                    isShowingPreviewCard = false
                }
                .onChange(of: isProgressComplete) { _, isCurrentlyComplete in
                    if !isCurrentlyComplete {
                        isShowingPreviewCard = false
                    }
                }
        }
    }
}

private extension Banner {
    var isProgressComplete: Bool {
        switch style {
        case .progress:
            return background.isComplete
        default:
            return false
        }
    }

    @ViewBuilder
    var auraView: some View {
        #if targetEnvironment(simulator)
            Color.black
        #else
            CircleMaskedAuraView(
                auraCoordinator,
                morphSpeed: 0.06,
                scale: 0.9
            )
        #endif
    }

    var previewCardBubble: some View {
        ZStack {
            switch previewCardBubbleIcon {
            case .none, .download:
                Image.Assets.blank
                    .resizable()
                    .aspectRatio(contentMode: .fill)

            case .facebook:
                Image.ShareIcon.facebook
                    .resizable()
                    .aspectRatio(contentMode: .fill)

            case .instagram:
                Image.ShareIcon.instagram
                    .resizable()
                    .aspectRatio(contentMode: .fill)

            case .tiktok:
                Image.ShareIcon.tiktok
                    .resizable()
                    .aspectRatio(contentMode: .fill)
            }
        }
        .frame(width: 24.0, height: 24.0)
        .clipShape(.rect(cornerRadius: .infinity))
    }

    @ViewBuilder
    var previewCard: some View {
        HStack {
            let previewAnimationDuration: TimeInterval = 0.3
            Spacer()
            if let previewImageURL = previewImageURL {
                Color.red
                    .frame(width: 50, height: 90.0)
                    .overlay {
                        ZStack {
                            switch style {
                            case .progress(.shareAssetGeneration):
                                Color.clear
                                    .background {
                                        LoopingVideoPlayer(
                                            playableUrl: previewImageURL,
                                            restartBeforePlaying: false,
                                            isPlaying: Binding(
                                                get: { true },
                                                set: { _ in }
                                            ),
                                            overrideTime: nil,
                                            videoGravity: .resizeAspectFill,
                                            fallbackView: {
                                                Color.black
                                            }
                                        )
                                        .onAppear {
                                            Task {
                                                try await Task.sleep(for: .seconds(0.5))
                                                Task { @MainActor in
                                                    isShowingPreviewCard = true
                                                }
                                            }
                                        }
                                    }
                                    .opacity(isShowingPreviewCard ? 1.0 : 0.0)

                            default:
                                RemoteImage(url: previewImageURL.absoluteString, fallbackId: "previewImage")
                                    .opacity(isShowingPreviewCard ? 1.0 : .zero)
                                    .onAppear {
                                        Task {
                                            try await Task.sleep(for: .seconds(0.5))
                                            Task { @MainActor in
                                                isShowingPreviewCard = true
                                            }
                                        }
                                    }
                                Color.white.opacity(isShowingPreviewCard ? .zero : 1.0)
                            }
                        }
                    }
                    .opacity(isShowingPreviewCard ? 1.0 : .zero)
                    .clipShape(.rect(cornerRadius: 8.0))
                    .overlay {
                        previewCardBubble
                            .rotationEffect(.degrees(isShowingPreviewCard ? -10.0 : -20.0), anchor: .center)
                            .offset(x: -24.0, y: -42.0)
                            .shadow(radius: 4.0)
                            .opacity(isShowingPreviewCard ? 1.0 : 0.0)
                    }
                    .rotationEffect(.degrees(isShowingPreviewCard ? 10.0 : 20.0), anchor: .center)
                    .offset(y: isShowingPreviewCard ? 54.0 : 82.0)
                    .shadow(radius: 4.0)
                    .animation(.easeOut(duration: previewAnimationDuration), value: isShowingPreviewCard)

            } else {
                EmptyView()
            }
        }
        .padding(.horizontal, 32.0)
    }

    @ViewBuilder
    var ancillaryIcon: some View {
        switch style {
        case .progress(let subStyle):
            if background.isComplete {
                switch subStyle {
                case .clipGeneration:
                    Circle()
                        .fill(.white.opacity(0.1))
                        .frame(width: 32.0, height: 32.0)
                        .overlay {
                            Image.Icon.playFilled
                                .resizable()
                                .frame(width: 16.0, height: 16.0)
                                .foregroundStyle(.white)
                        }

                case .shareAssetGeneration:
                    Circle()
                        .fill(.clear)
                        .frame(width: 32.0, height: 32.0)
                        .overlay {
                            Image.Icon.checkCircle
                                .resizable()
                                .frame(width: 32.0, height: 32.0)
                                .foregroundStyle(.white)
                        }
                }
            } else {
                GradientSpinner(size: .custom(width: 32, height: 32, lineWidth: 8.0))
            }

        default:
            EmptyView()
        }
    }

    @ViewBuilder
    var primaryViewText: some View {
        Group {
            switch message {
            case .string(let string):
                Text(string)
                    .fixedSize(horizontal: false, vertical: true)

            case .attributedString(let attributedString):
                Text(attributedString)
                    .fixedSize(horizontal: false, vertical: true)
            }
        }
        .drawingGroup()
        .foregroundColor(Color.SemanticV1.textPrimary)
        .typographyV1(.body1)
        .multilineTextAlignment(.leading)
        .lineLimit(4)
        .padding(.leading, 12.0)
    }

    @ViewBuilder
    var dismissView: some View {
        if style.allowDismiss {
            Button {
                dismissAction()
            } label: {
                ZStack(alignment: .center) {
                    Circle()
                        .fill(style.fillColor)
                    Image.Icon.close
                        .foregroundColor(Color.SemanticV1.textPrimary)
                }
                .frame(width: 40, height: 40)
                .clipShape(Rectangle())
            }
            .buttonStyle(.plain)
        }
    }
}

struct Banner_Previews: PreviewProvider {
    static var previews: some View {
        VStack(spacing: 24) {
            Banner(
                auraCoordinator: .init(),
                message: .string("This is a warning message"),
                style: .warning,
                tapAction: {},
                dismissAction: {}
            )

            Banner(
                auraCoordinator: .init(),
                message: .string("This is success!"),
                style: .success(.clipGeneration),
                tapAction: {},
                dismissAction: {}
            )

            Banner(
                auraCoordinator: .init(),
                message: .string("This is information"),
                style: .information,
                tapAction: {},
                dismissAction: {}
            )

            Banner(
                auraCoordinator: .init(),
                message: .string("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean varius vitae arcu quis lacinia. Donec mattis orci tellus, vitae gravida elit consectetur et. Aliquam ac tempor tortor, ac interdum diam. Fusce eu diam aliquam, tristique magna quis, molestie ipsum. Fusce sit amet mauris consequat, convallis orci in, semper ipsum."),
                style: .information,
                tapAction: {},
                dismissAction: {}
            )

            Banner(
                auraCoordinator: .init(),
                message: .string("Things are happening!"),
                style: .progress(.clipGeneration),
                background: .auraShader(completionRatio: .zero),
                tapAction: {},
                dismissAction: {}
            )

            Banner(
                auraCoordinator: .init(),
                message: .string("Custom message"),
                style: .custom(Image.Icon.handHorns),
                tapAction: {},
                dismissAction: {}
            )
        }
        .padding()
    }
}
