import Adamantium
import AnalyticsClient
import APIClient
import AVFoundation
import ClerkClient
import ComponentLibrary
import ComposableArchitecture
import Foundation
import Localization
import StatsigClient
import SwiftUI
import Utilities

@Reducer
public struct Welcome {
    @ObservableState
    public struct State: Equatable {
        // We can't make any decisions based on statsig flags nor experiments until this is set to true
        // This is set to true once the async operation of setting the statsig user has completed
        //
        // Until we remove the OnboardingCoordinator, we always route to the OnboardingCoordinator, rather
        // than the OnboardingReducer, since we want to be able to gate the OnboardingReducer with a feature flag
        public var isStatsigReady = false

        public init() {}
    }

    public enum Action {
        public enum Delegate {
            case onAppear
            case createAccountTapped
            case loginTapped
        }

        case onAppear
        case task
        case statsigStateChanged(StatsigClient.State)
        case createAccountTapped
        case loginTapped
        case delegate(Delegate)
    }

    @Dependency(AnalyticsClient.self) var analyticsClient
    @Dependency(StatsigClient.self) var statsigClient

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .onAppear:
                return .send(.delegate(.onAppear))

            case .task:
                struct StatsigStateCancellableId: Hashable {}
                let statsigState = statsigClient.currentState()
                state.isStatsigReady = statsigState.isReadyForWelcome

                // Watch Statsig's state so we can update isStatsigReady
                return .stream(
                    statsigClient.state(),
                    send: Action.statsigStateChanged,
                    cancellableId: StatsigStateCancellableId()
                )

            case let .statsigStateChanged(statsigState):
                state.isStatsigReady = statsigState.isReadyForWelcome
                return .none

            case .createAccountTapped:
                UIImpactFeedbackGenerator(style: .light).impactOccurred()
                return .send(.delegate(.createAccountTapped))

            case .loginTapped:
                UIImpactFeedbackGenerator(style: .light).impactOccurred()
                return .send(.delegate(.loginTapped))

            case .delegate:
                return .none
            }
        }

        Analytics()
    }
}

private extension StatsigClient.State {
    var isReadyForWelcome: Bool {
        switch self {
        case .initialized,
             .initializedWithUser:
            true
        case .initializing,
             .uninitialized:
            false
        }
    }
}

public struct WelcomeScreen: View {
    var store: StoreOf<Welcome>
    var landingVideoAsset: AVAsset

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

        guard let url = Bundle.componentLibraryResourceUrl(forResource: String.VideoAsset.landing, withExtension: "mp4") else {
            assertionFailure("Landing video is missing from bundle.")
            self.landingVideoAsset = AVAsset()
            return
        }
        self.landingVideoAsset = AVAsset(url: url)
    }

    public var body: some View {
        VStack(spacing: 0) {
            Spacer()

            Text(L10n.FeatureOnboarding.welcomeTitle)
                .typographyV1(.welcomeTitle)
                .foregroundStyle(Color.welcomeForegroundPrimary)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(.bottom, 12)

            Text(L10n.FeatureOnboarding.welcomeSubtitle)
                .typographyV1(.welcomeSubtitle)
                .foregroundStyle(Color.welcomeForegroundSecondary)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(.bottom, 32)

            createAccountButton
                .padding(.bottom, 16)

            signInSubtitle
        }
        .padding(29)
        .background(landingVideo)
        .background(Color.welcomeBackgroundPlaceholder) /// Displays briefly before the video loads in
        .dynamicTypeSize((.small) ... .xxLarge)
        .environment(\.colorScheme, .light)
        .onAppear { store.send(.onAppear) }
        .task { store.send(.task) }
    }

    var createAccountButton: some View {
        Button {
            store.send(.createAccountTapped)
        } label: {
            Text(L10n.FeatureOnboarding.welcomeCreateButton)
                .typographyV1(.welcomeCreateAccountButton)
                .foregroundStyle(Color.welcomeForegroundPrimary)
                .padding(.vertical, 16)
                .frame(maxWidth: .infinity)
                .background {
                    #if targetEnvironment(simulator)
                        Color.orange
                    #else
                        AuraShaderView(
                            id: "welcome-screen",
                            appPreset: .pinkYellowOrange,
                            morphSpeed: 0.05,
                            scale: 0.2,
                            seed: 0
                        )
                    #endif
                }
                .shadow(color: .black.opacity(0.2), radius: 3, x: 2, y: 2)
                .shadow(color: .black.opacity(0.1), radius: 8, x: 6, y: 6)
                .clipShape(Capsule())
        }
    }

    var signInSubtitle: some View {
        HStack(spacing: 8) {
            Text(L10n.FeatureOnboarding.welcomeAlreadyHaveAccount)
                .typographyV1(.signInSubtitle)
                .foregroundStyle(Color.welcomeForegroundTertiary)

            Button {
                store.send(.loginTapped)
            } label: {
                Text(L10n.FeatureOnboarding.welcomeSignIn)
                    .typographyV1(.signInSubtitle)
                    .foregroundStyle(Color.welcomeForegroundPrimary)
            }
        }
    }

    var landingVideo: some View {
        ZStack {
            WelcomeLoopingVideoPlayer(
                asset: landingVideoAsset,
                restartBeforePlaying: false,
                isPlaying: .constant(true),
                fallbackView: {
                    Color.welcomeBackgroundPlaceholder
                },
                fadeIn: true
            )

            LinearGradient(
                /// Slightly darker on the bottom, and slightly more vibrant at the top
                gradient: Gradient(stops: [
                    .init(color: Color.clear, location: 0),
                    .init(color: Color.black.opacity(0.1), location: 0.3),
                    .init(color: Color.black.opacity(0.85), location: 0.7),
                    .init(color: Color.black, location: 1.0),
                ]),
                startPoint: .top,
                endPoint: .bottom
            )
        }
        .ignoresSafeArea()
    }
}

// MARK: Styling Extensions

/// These typography choices and colors are not reflected in our design system and only used for this view.

extension TypographyV1 {
    /// size: 60pt, weight: light
    static let welcomeTitle: TypographyV1 = .init(
        name: "Welcome Title",
        size: 60,
        style: .title,
        weight: .editorialNewLight
    )

    /// size: 20pt, weight: regular
    static let welcomeSubtitle: TypographyV1 = .init(
        name: "Welcome Subtitle",
        size: 20,
        style: .subheadline,
        weight: .ppNeueMontrealRegular
    )

    /// size: 20pt, weight: medium
    static let welcomeCreateAccountButton: TypographyV1 = .init(
        name: "Welcome Create Account Button",
        size: 20,
        style: .subheadline,
        weight: .ppNeueMontrealMedium
    )

    /// size: 15pt, weight: medium
    static let signInSubtitle: TypographyV1 = .init(
        name: "Sign In Subtitle",
        size: 15,
        style: .subheadline,
        weight: .ppNeueMontrealMedium
    )
}

extension Color {
    static let welcomeForegroundPrimary = Color.white
    static let welcomeForegroundSecondary = Color.white.opacity(0.7)
    static let welcomeForegroundTertiary = Color.white.opacity(0.5)

    static let welcomeBackgroundPlaceholder = Color.black
}

#Preview {
    SignUpScreen(store: .init(initialState: .init(method: .login)) {
        SignUp()
    })
}
