import Adamantium
import APIClient
import ComponentLibrary
import ComposableArchitecture
import ShareAssetClient
// Temporary until we phase out old Banner for BannerV2
import StatsigClient
import SwiftUI

@Reducer
public struct BannerReducer {
    let dismissDelay: Duration = .seconds(4)

    public enum Action {
        // Internal use only
        case setBanner(State.BannerType?)

        case show(type: State.BannerType, autoDismiss: Bool)
        case dismiss
        case tapped
    }

    @ObservableState
    public struct State: Equatable {
        public struct BannerType: Equatable {
            public enum Destination: Equatable {
                case appStore
                case newClipsInOmniPlayer
                case shareAsset(_ target: ShareAssetCreationTarget, _ localVideoURL: URL, _ attributionURL: URL)
                case hookDownload(_ hook: Hook, _ localVideoURL: URL)
            }

            public var message: Banner.Message
            public var style: Banner.Style
            public var background: Banner.Background = .none
            public var destination: Destination?
            public var previewImageURLs: [URL]?
            public var previewCardBubbleIcon: Banner.PreviewAncillaryBubbleIcon = .none

            public static func warning(_ message: Banner.Message, destination: Destination? = nil) -> BannerType {
                .init(message: message, style: .warning, destination: destination)
            }

            public static func success(_ message: Banner.Message, _ substyle: Banner.Style.SubStyle = .clipGeneration, destination: Destination? = nil, previewImageURLs: [URL]? = nil) -> BannerType {
                .init(
                    message: message,
                    style: .success(substyle),
                    destination: destination,
                    previewImageURLs: previewImageURLs
                )
            }

            public static func information(_ message: Banner.Message, destination: Destination? = nil) -> BannerType {
                .init(message: message, style: .information, destination: destination)
            }

            public static func custom(_ icon: Image, _ message: Banner.Message, destination: Destination? = nil) -> BannerType {
                .init(message: message, style: .custom(icon), destination: destination)
            }

            public static func progress(
                _ message: Banner.Message,
                completionRatio: Double,
                subStyle: Banner.Style.SubStyle = .clipGeneration,
                previewCardBubbleIcon: Banner.PreviewAncillaryBubbleIcon = .none,
                destination: Destination? = nil,
                previewImageURLs: [URL]? = nil
            ) -> BannerType {
                // Until we phase out Banner(V1) for BannerV2, we need to check the feature flag
                // here - this will be removed after releasing and monitoring the impact
                // of the updated banner designs
                let overrideWithSuccessBanner: Bool = FeatureFlag.legacy.songGenerationBannerV2
                let style: Banner.Style = overrideWithSuccessBanner && completionRatio == 1.0 ? .success(subStyle) : .progress(subStyle)
                return .init(
                    message: message,
                    style: style,
                    background: .auraShader(completionRatio: completionRatio),
                    destination: destination,
                    previewImageURLs: previewImageURLs,
                    previewCardBubbleIcon: previewCardBubbleIcon
                )
            }
        }

        public var isBannerShown = false
        public var didDismissBeforeCompletion: Bool = false
        public var auraCoordinator = CircleMaskedAuraView.Coordinator()
        public var banner: BannerType?

        public init() {}
    }

    @Dependency(\.mainQueue) var mainQueue

    public init() {}

    public var body: some Reducer<State, Action> {
        Reduce { state, action in
            struct ToastDismissCancellableID: Hashable {}
            struct ToastAppearCancellableID: Hashable {}

            switch action {
            case .setBanner(let banner):
                if let banner {
                    let auraPropertyTarget = BannerReducer.currentAura(banner.background)
                    state.auraCoordinator.setGradientSeed(125.0)
                    if banner.background.isJustStarting {
                        let zeroPropertyTarget = BannerReducer.zeroAuraTarget(BannerReducer.colorSetForCompletionRatio(0.2))
                        state.auraCoordinator.applyPropertiesWithoutTransitionAnimation(zeroPropertyTarget)
                        state.auraCoordinator.setAnimationTimeScalar(2.0)
                        state.auraCoordinator.setPropertyTarget(auraPropertyTarget)
                    } else {
                        state.auraCoordinator.setAnimationTimeScalar(banner.background.isComplete ? 2.0 : 0.5)
                        state.auraCoordinator.setPropertyTarget(auraPropertyTarget)
                    }
                }
                state.banner = banner
                return .none

            case let .show(type, autoDismiss):
                switch type.style {
                case .progress:
                    if !state.isBannerShown {
                        let zeroPropertyTarget = BannerReducer.zeroAuraTarget(BannerReducer.colorSetForCompletionRatio(0.2))
                        state.auraCoordinator.applyPropertiesWithoutTransitionAnimation(zeroPropertyTarget)
                        state.isBannerShown = true
                    }

                case .success:
                    if state.didDismissBeforeCompletion {
                        state.didDismissBeforeCompletion = false
                        return .none
                    }

                default:
                    break
                }

                guard state.didDismissBeforeCompletion == false else { return .none }

                if autoDismiss {
                    return .concatenate(
                        .cancel(id: ToastDismissCancellableID()),
                        .cancel(id: ToastAppearCancellableID()),
                        .run { [type = type] send in
                            try await Task.sleep(for: .seconds(0.3))
                            await send(.setBanner(type), animation: .default)
                        }.cancellable(id: ToastAppearCancellableID()),
                        .run { send in // Auto dismiss
                            try await Task.sleep(for: dismissDelay)
                            await send(.dismiss, animation: .default)
                        }.cancellable(id: ToastDismissCancellableID())
                    )
                } else {
                    return .concatenate(
                        .cancel(id: ToastAppearCancellableID()),
                        .run { [type = type] send in
                            try await Task.sleep(for: .seconds(0.3))
                            await send(.setBanner(type), animation: .default)
                        }.cancellable(id: ToastAppearCancellableID())
                    )
                }

            case .dismiss:
                if state.isBannerShown {
                    let zeroPropertyTarget = BannerReducer.zeroAuraTarget(BannerReducer.colorSetForCompletionRatio(0.2))
                    state.auraCoordinator.setPropertyTarget(zeroPropertyTarget)
                    if let banner = state.banner, case .progress = banner.style {
                        state.didDismissBeforeCompletion = true
                    }
                }
                state.isBannerShown = false
                return .send(.setBanner(nil), animation: .default)

            case .tapped:
                // Handled in parent reducer

                return .none
            }
        }
    }
}

private extension BannerReducer {
    static func colorSetForCompletionRatio(_ ratio: Double) -> SIMDColorSet {
        return ratio >= 1.0 ? .Preset.bannerComplete.colorSet : .Preset.bannerInProgress.colorSet
    }

    static func currentAura(_ background: Banner.Background) -> CircleMaskedAuraView.Coordinator.PropertyTarget {
        guard case .auraShader(let ratio) = background else {
            let colorSet = BannerReducer.colorSetForCompletionRatio(.zero)
            return zeroAuraTarget(colorSet)
        }

        let colorSet = BannerReducer.colorSetForCompletionRatio(ratio)
        return zeroAuraTarget(colorSet).mix(fullAuraTarget(colorSet), ratio: Float(ratio))
    }

    static func zeroAuraTarget(_ colorSet: SIMDColorSet) -> CircleMaskedAuraView.Coordinator.PropertyTarget {
        let darkColor = SIMDColorSet.Preset.bannerEmpty.colorSet
        return .init(
            mainColorA: colorSet.a,
            mainColorB: colorSet.b,
            mainColorC: colorSet.c,
            darkColorA: darkColor.a,
            darkColorB: darkColor.b,
            darkColorC: darkColor.c,
            maskPosX: -1.0,
            maskPosY: 0.500,
            maskEdgeFade: 3.0,
            maskMinValueScalar: 0.55,
            maskRadius: 0.250
        )
    }

    static func fullAuraTarget(_ colorSet: SIMDColorSet) -> CircleMaskedAuraView.Coordinator.PropertyTarget {
        let darkColor = SIMDColorSet.Preset.bannerEmpty.colorSet
        return .init(
            mainColorA: colorSet.a,
            mainColorB: colorSet.b,
            mainColorC: colorSet.c,
            darkColorA: darkColor.a,
            darkColorB: darkColor.b,
            darkColorC: darkColor.c,
            maskPosX: 0.5,
            maskPosY: 0.499,
            maskEdgeFade: 3.00,
            maskMinValueScalar: 0.55,
            maskRadius: 0.225
        )
    }
}

public struct BannerView: View {
    let store: StoreOf<BannerReducer>

    public init(store: StoreOf<BannerReducer>) {
        self.store = store
    }

    public var body: some View {
        ZStack {
            switch store.banner {
            case .some(let banner):
                Banner(
                    auraCoordinator: store.auraCoordinator,
                    message: banner.message,
                    style: banner.style,
                    previewCardBubbleIcon: banner.previewCardBubbleIcon,
                    background: banner.background,
                    tapAction: { store.send(.tapped) },
                    previewImageURL: banner.previewImageURLs?.first,
                    dismissAction: { store.send(.dismiss, animation: .default) }
                )
                .transition(.move(edge: .top).combined(with: .opacity))

            case .none:
                EmptyView()
            }
        }
        .animation(.spring(), value: store.banner)
    }
}

public struct BannerViewV2: View {
    let store: StoreOf<BannerReducer>

    public init(store: StoreOf<BannerReducer>) {
        self.store = store
    }

    public var body: some View {
        ZStack {
            switch store.banner {
            case .some(let banner):
                BannerV2(
                    auraCoordinator: store.auraCoordinator,
                    message: banner.message,
                    style: banner.style,
                    previewCardBubbleIcon: banner.previewCardBubbleIcon,
                    background: banner.background,
                    tapAction: { store.send(.tapped) },
                    previewImageURLs: banner.previewImageURLs,
                    dismissAction: { store.send(.dismiss, animation: .default) }
                )
                .transition(.move(edge: .top).combined(with: .opacity))

            case .none:
                EmptyView()
            }
        }
        .animation(.bouncy, value: store.banner)
    }
}
