import SwiftUI
import StoreKit
import ComposableArchitecture
import Generated
import Models
import NotEnoughFreeSpace
import RecoveryPhraseDisplay
import Welcome
import ExportLogs
import OnboardingFlow
import Sandbox
import Tabs
import ZcashLightClientKit
import UIComponents

public struct RootView: View {
    @Environment(\.scenePhase) var scenePhase
    @State var covered = false
    
    let store: RootStore
    let tokenName: String
    let networkType: NetworkType

    public init(store: RootStore, tokenName: String, networkType: NetworkType) {
        self.store = store
        self.tokenName = tokenName
        self.networkType = networkType
    }
    
    public var body: some View {
        switchOverDestination()
            .overlay {
                if covered {
                    VStack {
                        ZashiIcon()
                            .scaleEffect(2.0)
                            .padding(.bottom, 180)
                    }
                    .applyScreenBackground()
                }
            }
            .onChange(of: scenePhase) { value in
                covered = value == .background
            }
    }
}

private struct FeatureFlagWrapper: Identifiable, Equatable, Comparable {
    let name: FeatureFlag
    let isEnabled: Bool
    var id: String { name.rawValue }

    static func < (lhs: FeatureFlagWrapper, rhs: FeatureFlagWrapper) -> Bool {
        lhs.name.rawValue < rhs.name.rawValue
    }

    static func == (lhs: FeatureFlagWrapper, rhs: FeatureFlagWrapper) -> Bool {
        lhs.name.rawValue == rhs.name.rawValue
    }
}

private extension RootView {
    @ViewBuilder func switchOverDestination() -> some View {
        WithViewStore(store, observe: { $0 }) { viewStore in
            Group {
                switch viewStore.destinationState.destination {
                case .notEnoughFreeSpace:
                    NavigationView {
                        NotEnoughFreeSpaceView(
                            store: store.scope(
                                state: \.notEnoughFreeSpaceState,
                                action: RootReducer.Action.notEnoughFreeSpace
                            )
                        )
                    }
                    .navigationViewStyle(.stack)
                    .overlayedWithSplash(viewStore.splashAppeared) {
                        viewStore.send(.splashRemovalRequested)
                    }

                case .tabs:
                    NavigationView {
                        TabsView(
                            store: store.scope(
                                state: \.tabsState,
                                action: RootReducer.Action.tabs
                            ),
                            tokenName: tokenName,
                            networkType: networkType
                        )
                    }
                    .navigationViewStyle(.stack)
                    .overlayedWithSplash(viewStore.splashAppeared) {
                        viewStore.send(.splashRemovalRequested)
                    }
                    
                case .phraseDisplay:
                    NavigationView {
                        RecoveryPhraseDisplayView(
                            store: store.scope(
                                state: \.phraseDisplayState,
                                action: RootReducer.Action.phraseDisplay
                            )
                        )
                    }
                    .overlayedWithSplash(viewStore.splashAppeared) {
                        viewStore.send(.splashRemovalRequested)
                    }

                case .sandbox:
                    NavigationView {
                        SandboxView(
                            store: store.scope(
                                state: \.sandboxState,
                                action: RootReducer.Action.sandbox
                            ),
                            tokenName: tokenName,
                            networkType: networkType
                        )
                    }
                    .navigationViewStyle(.stack)
                    
                case .onboarding:
                    NavigationView {
                        PlainOnboardingView(
                            store: store.scope(
                                state: \.onboardingState,
                                action: RootReducer.Action.onboarding
                            )
                        )
                    }
                    .navigationViewStyle(.stack)
                    .overlayedWithSplash(viewStore.splashAppeared) {
                        viewStore.send(.splashRemovalRequested)
                    }

                case .startup:
                    ZStack(alignment: .topTrailing) {
                        debugView(viewStore)
                            .transition(.opacity)
                    }
                      
                case .welcome:
                    WelcomeView(
                        store: store.scope(
                            state: \.welcomeState,
                            action: RootReducer.Action.welcome
                        )
                    )
                }
            }
            .onOpenURL(perform: { viewStore.goToDeeplink($0) })
            .alert(store: store.scope(
                state: \.$alert,
                action: { .alert($0) }
            ))
            .alert(store: store.scope(
                state: \.exportLogsState.$alert,
                action: { .exportLogs(.alert($0)) }
            ))

            shareLogsView(viewStore)
        }
    }
}

private extension RootView {
    @ViewBuilder func shareLogsView(_ viewStore: RootViewStore) -> some View {
        if viewStore.exportLogsState.isSharingLogs {
            UIShareDialogView(
                activityItems: viewStore.exportLogsState.zippedLogsURLs
            ) {
                viewStore.send(.exportLogs(.shareFinished))
            }
            // UIShareDialogView only wraps UIActivityViewController presentation
            // so frame is set to 0 to not break SwiftUIs layout
            .frame(width: 0, height: 0)
        } else {
            EmptyView()
        }
    }

    @ViewBuilder func debugView(_ viewStore: RootViewStore) -> some View {
        VStack(alignment: .leading) {
            if viewStore.destinationState.previousDestination == .tabs {
                Button(L10n.General.back.uppercased()) {
                    viewStore.goToDestination(.tabs)
                }
                .zcashStyle()
                .frame(width: 150)
                .padding()
            }

            List {
                Section(header: Text(L10n.Root.Debug.title)) {
                    Button(L10n.Root.Debug.Option.exportLogs) {
                        viewStore.send(.exportLogs(.start))
                    }
                    .disabled(viewStore.exportLogsState.exportLogsDisabled)

                    Button(L10n.Root.Debug.Option.testCrashReporter) {
                        viewStore.send(.debug(.testCrashReporter))
                    }

#if DEBUG
                    Button(L10n.Root.Debug.Option.appReview) {
                        viewStore.send(.debug(.rateTheApp))
                        if let currentScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
                            SKStoreReviewController.requestReview(in: currentScene)
                        }
                    }
#endif
                    
                    Button(L10n.Root.Debug.Option.copySeed) {
                        viewStore.send(.debug(.copySeedToPasteboard))
                    }

                    Button(L10n.Root.Debug.Option.rescanBlockchain) {
                        viewStore.send(.debug(.rescanBlockchain))
                    }

                    Button(L10n.Root.Debug.Option.nukeWallet) {
                        viewStore.send(.initialization(.nukeWalletRequest))
                    }
                }
            }
            .confirmationDialog(
              store: self.store.scope(state: \.$confirmationDialog, action: { .confirmationDialog($0) })
            )
        }
        .navigationBarTitle(L10n.Root.Debug.navigationTitle)
    }
}

// MARK: - Previews

struct RootView_Previews: PreviewProvider {
    static var previews: some View {
        NavigationView {
            RootView(
                store: RootStore(
                    initialState: .initial
                ) {
                    RootReducer()
                },
                tokenName: "ZEC",
                networkType: .testnet
            )
        }
    }
}
