import Observation
import SwiftUI

enum PhoneRoute: Hashable {
    case chat(String)
    case search(String)
    case draftChat
    case draftSearch
    case settings

    static func from(selection: SidebarSelection) -> PhoneRoute {
        switch selection {
        case let .chat(chatID):
            return .chat(chatID)
        case let .search(searchID):
            return .search(searchID)
        case .settings:
            return .settings
        }
    }
}

struct SybilPhoneShellView: View {
    @Bindable var viewModel: SybilViewModel
    @State private var path: [PhoneRoute] = []
    @Environment(\.scenePhase) private var scenePhase
    @State private var shouldRefreshOnForeground = false
    @State private var composerFocusRequest = 0

    var body: some View {
        NavigationStack(path: $path) {
            SybilPhoneSidebarRoot(viewModel: viewModel, path: $path)
                .navigationTitle("")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .topBarLeading) {
                        SybilWordmark(size: 21)
                    }
                }
                .navigationDestination(for: PhoneRoute.self) { route in
                    SybilPhoneDestinationView(
                        viewModel: viewModel,
                        path: $path,
                        composerFocusRequest: $composerFocusRequest,
                        route: route
                    )
                }
        }
        .tint(SybilTheme.primary)
        .onChange(of: scenePhase) { _, nextPhase in
            switch nextPhase {
            case .background:
                shouldRefreshOnForeground = true
                viewModel.markAppInactiveForNetwork()
            case .active:
                viewModel.markAppActiveForNetwork()
                guard shouldRefreshOnForeground else {
                    return
                }
                shouldRefreshOnForeground = false
                Task {
                    await viewModel.refreshAfterAppBecameActive(
                        refreshCollections: path.isEmpty,
                        refreshSelection: !path.isEmpty && viewModel.hasRefreshableSelection
                    )
                }
            case .inactive:
                shouldRefreshOnForeground = true
                viewModel.markAppInactiveForNetwork()
            @unknown default:
                break
            }
        }
    }
}

private struct SybilPhoneSidebarRoot: View {
    @Bindable var viewModel: SybilViewModel
    @Binding var path: [PhoneRoute]
    @State private var openingSelection: SidebarSelection?
    @State private var openingRequestID: UUID?

    private var highlightedSelection: SidebarSelection? {
        if let openingSelection {
            return openingSelection
        }

        guard let route = path.last else {
            return nil
        }

        switch route {
        case let .chat(chatID):
            return .chat(chatID)
        case let .search(searchID):
            return .search(searchID)
        case .draftChat, .draftSearch, .settings:
            return nil
        }
    }

    var body: some View {
        VStack(spacing: 0) {
            if let errorMessage = viewModel.errorMessage {
                Text(errorMessage)
                    .font(.sybil(.footnote))
                    .foregroundStyle(SybilTheme.danger)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal, 12)
                    .padding(.vertical, 10)

                Divider()
                    .overlay(SybilTheme.border)
            }

            if viewModel.isLoadingCollections && viewModel.sidebarItems.isEmpty {
                VStack(alignment: .leading, spacing: 8) {
                    ProgressView()
                        .tint(SybilTheme.primary)
                    Text("Loading conversations…")
                        .font(.sybil(.footnote))
                        .foregroundStyle(SybilTheme.textMuted)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                .padding(16)
            } else if viewModel.sidebarItems.isEmpty {
                VStack(spacing: 10) {
                    Image(systemName: "message.badge")
                        .font(.system(size: 20, weight: .medium))
                        .foregroundStyle(SybilTheme.textMuted)
                    Text("Start a chat or run your first search.")
                        .font(.sybil(.footnote))
                        .multilineTextAlignment(.center)
                        .foregroundStyle(SybilTheme.textMuted)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .padding(16)
            } else {
                ScrollView {
                    LazyVStack(alignment: .leading, spacing: 0) {
                        ForEach(viewModel.sidebarItems) { item in
                            Button {
                                open(item.selection)
                            } label: {
                                VStack(spacing: 0.0) {
                                    SybilPhoneSidebarRow(item: item)
                                    Divider()
                                }
                            }
                            .buttonStyle(
                                SybilPhoneSidebarRowButtonStyle(
                                    isHighlighted: highlightedSelection == item.selection
                                )
                            )
                            .contextMenu {
                                Button(role: .destructive) {
                                    Task {
                                        await viewModel.deleteItem(item.selection)
                                    }
                                } label: {
                                    Label("Delete", systemImage: "trash")
                                }
                            }
                        }
                    }
                }
            }
        }
        .background(SybilTheme.panelGradient)
        .safeAreaInset(edge: .bottom, spacing: 0) {
            bottomToolbar
        }
    }

    private var bottomToolbar: some View {
        VStack(spacing: 0) {
            Divider()
                .overlay(SybilTheme.border)

            HStack(spacing: 12) {
                toolbarIconButton(systemImage: "gearshape", accessibilityLabel: "Settings") {
                    clearOpeningSelection()
                    path = [.settings]
                }

                Spacer()

                toolbarIconButton(systemImage: "magnifyingglass", accessibilityLabel: "New search") {
                    clearOpeningSelection()
                    viewModel.startNewSearch()
                    path = [.draftSearch]
                }

                toolbarIconButton(systemImage: "plus", accessibilityLabel: "New chat", isPrimary: true) {
                    clearOpeningSelection()
                    viewModel.startNewChat()
                    path = [.draftChat]
                }
            }
            .padding(.horizontal, 18)
            .padding(.vertical, 10)
            .background(SybilTheme.panelGradient)
        }
    }

    private func toolbarIconButton(
        systemImage: String,
        accessibilityLabel: String,
        isPrimary: Bool = false,
        action: @escaping () -> Void
    ) -> some View {
        Button(action: action) {
            Image(systemName: systemImage)
                .font(.system(size: 18, weight: .semibold))
                .foregroundStyle(isPrimary ? SybilTheme.text : SybilTheme.textMuted)
                .frame(width: 42, height: 42)
                .background(
                    Circle()
                        .fill(
                            isPrimary
                                ? AnyShapeStyle(SybilTheme.primaryGradient)
                                : AnyShapeStyle(SybilTheme.surface.opacity(0.78))
                        )
                )
                .overlay(
                    Circle()
                        .stroke(isPrimary ? SybilTheme.primary.opacity(0.42) : SybilTheme.border.opacity(0.76), lineWidth: 1)
                )
        }
        .buttonStyle(.plain)
        .accessibilityLabel(accessibilityLabel)
    }

    private func clearOpeningSelection() {
        openingRequestID = nil
        openingSelection = nil
    }

    private func open(_ selection: SidebarSelection) {
        guard openingSelection != selection else {
            return
        }

        let requestID = UUID()
        openingRequestID = requestID
        openingSelection = selection
        Task {
            await viewModel.selectForNavigation(selection)
            guard openingRequestID == requestID else {
                return
            }
            path = [PhoneRoute.from(selection: selection)]
            openingRequestID = nil
            openingSelection = nil
        }
    }
}

private struct SybilPhoneSidebarRowIsActiveKey: EnvironmentKey {
    static let defaultValue = false
}

private extension EnvironmentValues {
    var sybilPhoneSidebarRowIsActive: Bool {
        get { self[SybilPhoneSidebarRowIsActiveKey.self] }
        set { self[SybilPhoneSidebarRowIsActiveKey.self] = newValue }
    }
}

private struct SybilPhoneSidebarRowButtonStyle: ButtonStyle {
    var isHighlighted: Bool

    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .environment(\.sybilPhoneSidebarRowIsActive, isHighlighted || configuration.isPressed)
    }
}

private struct SybilPhoneSidebarRow: View {
    @Environment(\.sybilPhoneSidebarRowIsActive) private var isHighlighted
    var item: SidebarItem

    var body: some View {
        let leadingWidth = 22.0
        
        VStack(alignment: .leading, spacing: 8) {
            HStack(spacing: 8) {
                Image(systemName: item.kind == .chat ? "message" : "globe")
                    .font(.system(size: 12, weight: .semibold))
                    .foregroundStyle(isHighlighted ? SybilTheme.accent : SybilTheme.textMuted)
                    .frame(width: leadingWidth, height: leadingWidth)
                    .background(
                        Rectangle()
                            .fill(isHighlighted ? SybilTheme.accent.opacity(0.12) : SybilTheme.surface.opacity(0.72))
                            
                    )

                Text(item.title)
                    .font(.sybil(.subheadline, weight: .semibold))
                    .lineLimit(1)
            }

            HStack(spacing: 8) {
                Spacer()
                    .frame(width: leadingWidth)

                Text(item.updatedAt.sybilRelativeLabel)
                    .font(.sybil(.caption2))
                    .foregroundStyle(SybilTheme.textMuted)

                if let initiated = item.initiatedLabel {
                    Spacer(minLength: 0)
                    Text(initiated)
                        .font(.sybil(.caption2))
                        .foregroundStyle(SybilTheme.textMuted.opacity(0.88))
                        .lineLimit(1)
                        .multilineTextAlignment(.trailing)
                        .frame(maxWidth: .infinity, alignment: .trailing)
                }
            }
        }
        .foregroundStyle(SybilTheme.text)
        .padding(18.0)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(
            Rectangle()
                .fill(
                    isHighlighted
                        ? SybilTheme.selectedRowGradient
                        : LinearGradient(colors: [SybilTheme.surface.opacity(0.56), SybilTheme.surface.opacity(0.36)], startPoint: .topLeading, endPoint: .bottomTrailing)
                )
        )

    }
}

private struct SybilPhoneDestinationView: View {
    @Bindable var viewModel: SybilViewModel
    @Binding var path: [PhoneRoute]
    @Binding var composerFocusRequest: Int
    let route: PhoneRoute

    var body: some View {
        SybilWorkspaceView(
            viewModel: viewModel,
            composerFocusRequest: composerFocusRequest,
            onRequestNewChat: {
                viewModel.startNewChat()
                composerFocusRequest += 1
                if path.isEmpty {
                    path = [.draftChat]
                } else {
                    path[path.index(before: path.endIndex)] = .draftChat
                }
            }
        )
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .navigationBarTitleDisplayMode(.inline)
        .task(id: route) {
            applyRoute()
        }
    }

    private func applyRoute() {
        switch route {
        case let .chat(chatID):
            guard viewModel.draftKind != nil || viewModel.selectedItem != .chat(chatID) else {
                return
            }
            viewModel.select(.chat(chatID))
        case let .search(searchID):
            guard viewModel.draftKind != nil || viewModel.selectedItem != .search(searchID) else {
                return
            }
            viewModel.select(.search(searchID))
        case .draftChat:
            viewModel.startNewChat()
        case .draftSearch:
            viewModel.startNewSearch()
        case .settings:
            viewModel.openSettings()
        }
    }
}
