import Adamantium
import CachedAsyncImage
import SwiftUI

public struct BannerV2: View {
    @ObservedObject var auraCoordinator: CircleMaskedAuraView.Coordinator
    private var message: Banner.Message
    private var style: Banner.Style
    private var previewCardBubbleIcon: Banner.PreviewAncillaryBubbleIcon
    private var background: Banner.Background
    private var previewImageURLs: [URL]?
    private let tapAction: (() -> Void)?
    private let dismissAction: () -> Void

    @State var animateGradientSpinner = false
    @State private var dragOffset: CGSize = .zero
    @State private var isDragging: Bool = false
    @State private var shouldFanOutCoverArt: Bool = false
    @State private var auraOffset: CGFloat = -1.2

    var shouldShowColorGradient: Bool {
        switch style {
        case .progress, .success:
            return true
        default:
            return false
        }
    }

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

    public var body: some View {
        HStack(spacing: 12) {
            // Left: Asset preview with fan-out animation for clips
            // or asset preview + associated platform icon for Shares
            leftAssetView

            // Center: Text content
            VStack(alignment: .leading, spacing: 4) {
                primaryViewText
            }
            .frame(maxWidth: .infinity, alignment: .leading)

            // Right: Action button (play/close/checkmark)
            rightActionView
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 10)
        .frame(height: 72)
        .background(capsuleBackground)
        .offset(y: dragOffset.height)
        .padding(.horizontal, 10)
        .gesture(
            DragGesture()
                .onChanged { value in
                    // Only allow upward drag
                    if value.translation.height >= 0, !isDragging { return }
                    if value.translation.height < 0 {
                        isDragging = true
                        dragOffset = value.translation
                    }
                }
                .onEnded { value in
                    let dismissThreshold: CGFloat = -10

                    if value.translation.height < dismissThreshold {
                        // Swipe was far enough - dismiss with animation
                        withAnimation(.easeOut(duration: 0.3)) {
                            dragOffset = CGSize(width: 0, height: -200)
                        }

                        // Handle different banner types
                        dismissAction()
                    } else {
                        // Snap back to original position
                        withAnimation(.spring(response: 0.4, dampingFraction: 0.8)) {
                            dragOffset = .zero
                            isDragging = false
                        }
                    }
                }
        )
        .frame(height: 72)
        .environment(\.colorScheme, .dark)
        .onTapGesture {
            if case .progress = style {
                // Don't dismiss on Tap for loading banners
                return
            } else {
                // Handle tap action
                withAnimation(.easeOut(duration: 0.3)) {
                    dragOffset = CGSize(width: 0, height: -200)
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    tapAction?()
                }
            }
        }
    }

    private func startAnimations() {
        // Start aura shimmer ping pong animation with smoother timing
        withAnimation(.easeInOut(duration: 3.0).repeatForever(autoreverses: true)) {
            auraOffset = 1.2
        }
    }

    private var capsuleBackground: some View {
        Capsule()
            .fill(.ultraThinMaterial)
            .overlay {
                Capsule()
                    .fill(.clear)
                    .strokeBorder(Color.SemanticV2.backgroundFogThin, lineWidth: 1.0)
            }
            .overlay(
                // Aura shimmer effect
                LinearGradient(
                    stops: [
                        Gradient.Stop(color: Color(red: 1, green: 0.68, blue: 0.01).opacity(0), location: 0.00),
                        Gradient.Stop(color: Color(red: 1, green: 0.59, blue: 0.04), location: 0.25),
                        Gradient.Stop(color: Color(red: 1, green: 0.3, blue: 0.15), location: 0.50),
                        Gradient.Stop(color: Color(red: 1, green: 0.24, blue: 0.47), location: 0.75),
                        Gradient.Stop(color: Color(red: 1, green: 0.68, blue: 0.01).opacity(0), location: 1.00),
                    ],
                    startPoint: UnitPoint(x: 0, y: 0.36),
                    endPoint: UnitPoint(x: 0.97, y: 0.9)
                )
                .opacity(0.2)
                .scaleEffect(x: 1.8, y: 1.0, anchor: .center)
                .offset(x: auraOffset * 200) // Use fixed offset instead of geometry-based
                .clipShape(RoundedRectangle(cornerRadius: 100))
                .opacity(shouldShowColorGradient ? 1.0 : 0.0)
            )
            .shadow(color: .black.opacity(0.25), radius: 6, x: 0, y: 0)
            .onAppear {
                // Don't animate if we're not a progress or success toast
                guard shouldShowColorGradient else { return }
                startAnimations()
            }
    }
}

private extension BannerV2 {
    var hasShareAssetContent: Bool {
        switch style {
        case .progress(let substyle), .success(let substyle):
            return substyle == .shareAssetGeneration
        default: return false
        }
    }

    var hasClipContent: Bool {
        switch style {
        case .progress(let substyle), .success(let substyle):
            return substyle == .clipGeneration
        default: return false
        }
    }

    @ViewBuilder
    var leftAssetView: some View {
        ZStack {
            switch style {
            case .success where hasShareAssetContent:
                // Video preview for share asset generation - regardless of loading state
                if let previewImageURLs = previewImageURLs, let videoURL = previewImageURLs.first {
                    videoPreview(url: videoURL)
                } else {
                    fallbackIcon
                }

            case .success where hasClipContent:
                // Stacked image preview for clip generation - regardless of loading state
                if let previewImageURLs,
                   previewImageURLs.count >= 2
                {
                    stackedImagePreview(urls: previewImageURLs)
                } else if let previewImageURLs,
                          previewImageURLs.count == 1,
                          let url = previewImageURLs.first
                {
                    // Single image for "Get full clip" flows that only
                    // produce one clip
                    singleImagePreview(url: url)
                } else {
                    fallbackIcon
                }

            default:
                // Fallback icon for warning/info/other states
                fallbackIcon
            }
        }
    }

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

            case .download:
                Image.Icon.downloadTray
                    .resizable()
                    .frame(width: 16.0, height: 16.0)
                    .aspectRatio(contentMode: .fill)
                    .foregroundStyle(.white)
                    .background {
                        Circle()
                            .foregroundStyle(.black)
                            .frame(width: 24.0, height: 24.0)
                    }

            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
    func stackedImagePreview(urls: [URL]) -> some View {
        ZStack {
            ForEach(Array(urls.enumerated()), id: \.offset) { index, url in
                let isLeft = index % 2 == 0
                let offset: CGFloat = shouldFanOutCoverArt ? (isLeft ? -5 : 5) : 0
                let rotation: CGFloat = shouldFanOutCoverArt ? (isLeft ? -10 : 10) : 0
                RemoteImage(url: url.absoluteString, fallbackId: "1")
                    .frame(width: 24, height: 32)
                    .background(Color.SemanticV2.backgroundSmokeDense)
                    .clipShape(RoundedRectangle(cornerRadius: 6))
                    .rotationEffect(.degrees(rotation))
                    .offset(x: offset)
                    .animation(.easeInOut(duration: 0.3).delay(0.3), value: shouldFanOutCoverArt)
                    .shadow(color: Color.SemanticV2.backgroundSmokeDense, radius: 1, x: 0, y: 0)
            }
        }
        .opacity(shouldFanOutCoverArt ? 1.0 : 0.0)
        .frame(width: 40, height: 40)
        .onAppear {
            shouldFanOutCoverArt = true
        }
    }

    // For Remaster banners
    @ViewBuilder
    func singleImagePreview(url: URL) -> some View {
        RemoteImage(url: url.absoluteString, fallbackId: "1")
            .frame(width: 24, height: 32)
            .background(Color.SemanticV2.backgroundSmokeDense)
            .clipShape(RoundedRectangle(cornerRadius: 6))
            .shadow(color: Color.SemanticV2.backgroundSmokeDense, radius: 1, x: 0, y: 0)
            .padding(.leading, 8)
    }

    @ViewBuilder
    func videoPreview(url: URL) -> some View {
        RoundedRectangle(cornerRadius: 6)
            .fill(Color.gray.opacity(0.3))
            .frame(width: 24, height: 34)
            .overlay {
                // Video preview for share asset generation
                LoopingVideoPlayer(
                    playableUrl: url,
                    restartBeforePlaying: false,
                    isPlaying: .constant(true),
                    overrideTime: nil,
                    videoGravity: .resizeAspectFill,
                    fallbackView: {
                        RemoteImage(url: url.absoluteString, fallbackId: "video-preview")
                    }
                )
                .clipShape(RoundedRectangle(cornerRadius: 6))
            }
            .shadow(color: Color.SemanticV2.backgroundSmokeDense, radius: 1, x: 0, y: 0)
            .rotationEffect(.degrees(8))
            .offset(y: 5)
            .overlay {
                previewCardBubble
                    .offset(x: -9.0, y: -12.0)
                    .shadow(radius: 4.0)
                    .rotationEffect(.degrees(-8))
            }
            .padding(.leading, 14)
    }

    @ViewBuilder
    var fallbackIcon: some View {
        if let image = style.image {
            image
                .resizable()
                .frame(width: 20, height: 20, alignment: .center)
                .foregroundStyle(Color.SemanticV1.iconPrimary)
                .frame(width: 40, height: 40)
        } else {
            Rectangle()
                .fill(Color.clear)
                .frame(width: 8)
        }
    }

    @ViewBuilder
    var rightActionView: some View {
        switch style {
        case .progress:
            // Show spinner for in-progress states on RIGHT side (same as original banner)
            GradientSpinner(size: .extraLarge,
                            color: Color.SemanticV2.accentPink)
                .frame(width: 40, height: 40)

        case .success where hasShareAssetContent:
            Image.Icon.checkV1
                .resizable()
                .frame(width: 24, height: 24)
                .foregroundStyle(.white)
                .frame(width: 40, height: 40)

        case .success where hasClipContent:
            // Play button for completed state with aura background
            ZStack {
                #if targetEnvironment(simulator)
                    Color.orange
                #else
                    AuraShaderView(
                        id: "banner-play-button",
                        appPreset: .pinkYellowOrange,
                        morphSpeed: 0.025,
                        scale: 0.6,
                        seed: 0
                    )
                #endif
                Image.Icon.playFilled
                    .resizable()
                    .frame(width: 18, height: 18)
                    .foregroundStyle(.white)
            }
            .clipShape(Circle())
            .frame(width: 40, height: 40)

        default:
            if case .warning = style {
                dismissButton
                    .frame(width: 40, height: 40)
            } else {
                EmptyView()
            }
        }
    }

    @ViewBuilder
    var dismissButton: some View {
        Button(action: {
            dragOffset = CGSize(width: 0, height: -200)
            dismissAction()
        }) {
            Image.Icon.close
                .resizable()
                .frame(width: 20, height: 20)
                .foregroundStyle(Color.SemanticV2.foregroundTertiaryGlass)
        }
    }

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

    @ViewBuilder
    var primaryViewText: some View {
        Group {
            switch message {
            case .string(let string):
                Text(string)
                    .lineLimit(2)
                    .minimumScaleFactor(0.8)
                    .fixedSize(horizontal: false, vertical: true)
                    .contentTransition(.interpolate)
                    .animation(.easeInOut(duration: 0.3), value: message)

            // Keeping this for backward compatibility with Banner.swift,
            // but we don't show attributed strings in the new banner
            case .attributedString(let attributedString):
                Text(attributedString)
                    .fixedSize(horizontal: false, vertical: true)
            }
        }
        .foregroundColor(.white)
        .typographyV1(.body1)
        .multilineTextAlignment(.leading)
        .lineLimit(2)
    }
}

struct BannerV2_Previews: PreviewProvider {
    static var previews: some View {
        VStack(spacing: 24) {
            BannerV2(
                auraCoordinator: .init(),
                message: .string("Song ready, tap to play!"),
                style: .success(.clipGeneration),
                background: .auraShader(completionRatio: 1.0),
                tapAction: {},
                previewImageURLs: [URL(string: "https://example.com/image.jpg"), URL(string: "https://example.com/image.jpg")].compactMap { $0 },
                dismissAction: {}
            )

            BannerV2(
                auraCoordinator: .init(),
                message: .string("Share asset ready!"),
                style: .success(.shareAssetGeneration),
                previewCardBubbleIcon: .download,
                background: .auraShader(completionRatio: 1.0),
                tapAction: {},
                previewImageURLs: [URL(string: "https://example.com/video.mp4")].compactMap { $0 },
                dismissAction: {}
            )

            BannerV2(
                auraCoordinator: .init(),
                message: .string("Share asset ready!"),
                style: .success(.shareAssetGeneration),
                previewCardBubbleIcon: .instagram,
                background: .auraShader(completionRatio: 1.0),
                tapAction: {},
                previewImageURLs: [URL(string: "https://example.com/video.mp4")].compactMap { $0 },
                dismissAction: {}
            )
            BannerV2(
                auraCoordinator: .init(),
                message: .string("Your groove is on the way!"),
                style: .progress(.clipGeneration),
                background: .auraShader(completionRatio: 0.5),
                tapAction: {},
                previewImageURLs: nil,
                dismissAction: {}
            )

            BannerV2(
                auraCoordinator: .init(),
                message: .string("Something went wrong"),
                style: .warning,
                tapAction: {},
                dismissAction: {}
            )

            BannerV2(
                auraCoordinator: .init(),
                message: .string("Your lyrics contained some copyrighted material. Please upload your own lyrics or audio."),
                style: .warning,
                tapAction: {},
                dismissAction: {}
            )

            BannerV2(
                auraCoordinator: .init(),
                message: .string("There's a new version of Suno! Head to the App Store to update"),
                style: .custom(.Icon.arrowUp),
                tapAction: {},
                dismissAction: {}
            )
        }
        .background(Color.gray.opacity(0.2))
        .frame(width: 480)
        .fixedSize(horizontal: true, vertical: false)
    }
}
