import SwiftUI

public struct SplitView: View {
    @State private var viewModel = SybilViewModel()
    @ObservedObject private var quickActionRouter = SybilQuickActionRouter.shared
    @Environment(\.horizontalSizeClass) private var horizontalSizeClass
    @Environment(\.scenePhase) private var scenePhase
    @State private var shouldRefreshOnForeground = false
    @State private var composerFocusRequest = 0
    @State private var quickQuestionFocusRequest = 0
    @State private var hasPendingQuickQuestionPresentation = false
    @State private var isQuickQuestionPresented = false
    @State private var columnVisibility: NavigationSplitViewVisibility = .automatic

    private var keyboardActions: SybilKeyboardActions? {
        guard !viewModel.isCheckingSession, viewModel.isAuthenticated else {
            return nil
        }

        return SybilKeyboardActions(
            newChat: {
                viewModel.startNewChat()
                composerFocusRequest += 1
            },
            newSearch: {
                viewModel.startNewSearch()
                composerFocusRequest += 1
            },
            previousConversation: {
                viewModel.selectPreviousSidebarItem()
            },
            nextConversation: {
                viewModel.selectNextSidebarItem()
            }
        )
    }

    @MainActor public init() {
        SybilFontRegistry.registerIfNeeded()
        SybilTheme.applySystemAppearance()
    }

    public var body: some View {
        ZStack {
            SybilTheme.backgroundGradient
                .ignoresSafeArea()

            if viewModel.isCheckingSession {
                ProgressView("Checking session…")
                    .tint(SybilTheme.primary)
                    .foregroundStyle(SybilTheme.textMuted)
            } else if !viewModel.isAuthenticated {
                SybilConnectionView(viewModel: viewModel)
                    .padding()
            } else if horizontalSizeClass == .compact {
                SybilPhoneShellView(viewModel: viewModel)
            } else {
                GeometryReader { proxy in
                    NavigationSplitView(columnVisibility: $columnVisibility) {
                        SybilSidebarView(viewModel: viewModel)
                    } detail: {
                        SybilWorkspaceView(
                            viewModel: viewModel,
                            composerFocusRequest: composerFocusRequest,
                            navigationLeadingControl: splitNavigationLeadingControl(for: proxy.size),
                            onShowSidebar: showSidebar,
                            onRequestNewChat: {
                                viewModel.startNewChat()
                                composerFocusRequest += 1
                            }
                        )
                    }
                    .navigationSplitViewStyle(.balanced)
                    .tint(SybilTheme.primary)
                }
            }
        }
        .font(.sybil(.body))
        .preferredColorScheme(.dark)
        .focusedSceneValue(\.sybilKeyboardActions, keyboardActions)
        #if !targetEnvironment(macCatalyst)
        .sheet(isPresented: $isQuickQuestionPresented, onDismiss: handleQuickQuestionDismissed) {
            SybilQuickQuestionView(
                viewModel: viewModel,
                focusRequest: quickQuestionFocusRequest
            )
            .presentationDragIndicator(.visible)
        }
        #endif
        .task {
            #if targetEnvironment(macCatalyst)
            SybilMacQuickQuestionController.shared.attach(viewModel)
            #endif
            await viewModel.bootstrap()
            presentPendingQuickQuestionIfPossible()
        }
        .onReceive(quickActionRouter.$quickQuestionPresentationRequest) { request in
            guard request > 0 else {
                return
            }
            queueQuickQuestionPresentation()
        }
        .onChange(of: viewModel.isCheckingSession) { _, _ in
            presentPendingQuickQuestionIfPossible()
        }
        .onChange(of: viewModel.isAuthenticated) { _, _ in
            presentPendingQuickQuestionIfPossible()
        }
        .onChange(of: scenePhase) { _, nextPhase in
            switch nextPhase {
            case .background:
                shouldRefreshOnForeground = true
                viewModel.markAppInactiveForNetwork()
            case .active:
                #if targetEnvironment(macCatalyst)
                SybilMacQuickQuestionController.shared.attach(viewModel)
                #endif
                viewModel.markAppActiveForNetwork()
                guard shouldRefreshOnForeground, horizontalSizeClass != .compact else {
                    return
                }
                shouldRefreshOnForeground = false
                Task {
                    await viewModel.refreshAfterAppBecameActive(
                        refreshCollections: true,
                        refreshSelection: viewModel.hasRefreshableSelection
                    )
                }
            case .inactive:
                shouldRefreshOnForeground = true
                viewModel.markAppInactiveForNetwork()
            @unknown default:
                break
            }
        }
    }

    private func splitNavigationLeadingControl(for size: CGSize) -> SybilWorkspaceNavigationLeadingControl {
        return size.width < size.height ? .showSidebar : .hidden
    }

    private func showSidebar() {
        withAnimation(.easeInOut(duration: 0.22)) {
            columnVisibility = .all
        }
    }

    private func queueQuickQuestionPresentation() {
        hasPendingQuickQuestionPresentation = true
        presentPendingQuickQuestionIfPossible()
    }

    private func presentPendingQuickQuestionIfPossible() {
        guard hasPendingQuickQuestionPresentation,
              !viewModel.isCheckingSession,
              viewModel.isAuthenticated
        else {
            return
        }

        hasPendingQuickQuestionPresentation = false
        #if targetEnvironment(macCatalyst)
        SybilMacQuickQuestionController.shared.attach(viewModel)
        SybilMacQuickQuestionController.shared.show()
        #else
        quickQuestionFocusRequest += 1
        isQuickQuestionPresented = true
        #endif
    }

    private func handleQuickQuestionDismissed() {
        viewModel.cancelQuickQuestion()
    }
}

public struct SybilCommands: Commands {
    public static let workspaceWindowID = "sybil-workspace"

    @FocusedValue(\.sybilKeyboardActions) private var keyboardActions
    #if targetEnvironment(macCatalyst)
    @Environment(\.openWindow) private var openWindow
    @ObservedObject private var windowCommands = SybilMacWindowCommands.shared
    #endif

    public init() {}

    public var body: some Commands {
        CommandGroup(replacing: .newItem) {
            Button("New Chat") {
                #if targetEnvironment(macCatalyst)
                windowCommands.newChatOrWindow(
                    newChat: keyboardActions?.newChat,
                    openWindow: openNewWindow
                )
                #else
                keyboardActions?.newChat()
                #endif
            }
            .keyboardShortcut("n", modifiers: .command)
            #if targetEnvironment(macCatalyst)
            .disabled(!windowCommands.canStartNewChat(hasFocusedActions: keyboardActions != nil))
            #else
            .disabled(keyboardActions == nil)
            #endif

            #if targetEnvironment(macCatalyst)
            Button("New Window", action: openNewWindow)
                .keyboardShortcut("n", modifiers: [.command, .shift])
            #endif

            Button("New Search") {
                keyboardActions?.newSearch()
            }
            #if targetEnvironment(macCatalyst)
            .keyboardShortcut("n", modifiers: [.command, .option])
            #else
            .keyboardShortcut("n", modifiers: [.command, .shift])
            #endif
            .disabled(keyboardActions == nil)

            #if targetEnvironment(macCatalyst)
            Divider()
            Button("Quick Question") {
                SybilMacQuickQuestionController.shared.toggle()
            }
            .keyboardShortcut(.space, modifiers: .option)
            #endif
        }

        CommandMenu("Conversation") {
            Button("Previous Conversation") {
                keyboardActions?.previousConversation()
            }
            .keyboardShortcut("[", modifiers: .command)
            .disabled(keyboardActions == nil)

            Button("Next Conversation") {
                keyboardActions?.nextConversation()
            }
            .keyboardShortcut("]", modifiers: .command)
            .disabled(keyboardActions == nil)
        }
    }
    #if targetEnvironment(macCatalyst)
    private func openNewWindow() {
        // Targeting a WindowGroup without a value creates a fresh scene every
        // time, even when another workspace window is already open.
        openWindow(id: Self.workspaceWindowID)
    }
    #endif
}

private struct SybilKeyboardActions {
    var newChat: () -> Void
    var newSearch: () -> Void
    var previousConversation: () -> Void
    var nextConversation: () -> Void
}

private struct SybilKeyboardActionsKey: FocusedValueKey {
    typealias Value = SybilKeyboardActions
}

private extension FocusedValues {
    var sybilKeyboardActions: SybilKeyboardActions? {
        get { self[SybilKeyboardActionsKey.self] }
        set { self[SybilKeyboardActionsKey.self] = newValue }
    }
}
