import SwiftUI
import SwiftData
import VNCCore
import CoreGraphics
#if canImport(UIKit)
import UIKit
#endif

public struct SessionView: View {
    let connection: SavedConnection

    @Environment(\.modelContext) private var modelContext
    @Environment(\.dismiss) private var dismiss
    @Environment(\.scenePhase) private var scenePhase
    @AppStorage("defaultInputMode") private var defaultInputModeRaw = "touch"

    @State private var controller: SessionController?
    @State private var inputMode: InputMode = .touch
    @State private var showSoftwareKeyboard = false
    @State private var trackpadCursor: CGPoint = CGPoint(x: 0.5, y: 0.5)
    @State private var selectedScreenID: UInt32?
    @State private var screenshotItem: ScreenshotShareItem?
    @State private var chromeVisible = true

    public init(connection: SavedConnection) {
        self.connection = connection
    }

    public var body: some View {
        ZStack {
            Color.black.ignoresSafeArea()

            // Hidden accessibility probe for UI tests; reflects SwiftUI
            // state so we can verify binding propagation.
            Color.clear
                .frame(width: 1, height: 1)
                .accessibilityIdentifier("sessionview-state")
                .accessibilityLabel("kb=\(showSoftwareKeyboard)")

            if let controller {
                FramebufferView(
                    controller: controller,
                    inputMode: inputMode,
                    selectedScreen: selectedScreen(for: controller),
                    trackpadCursor: $trackpadCursor,
                    showSoftwareKeyboard: $showSoftwareKeyboard
                )
                .ignoresSafeArea()
                .onTapGesture(count: 3) {
                    withAnimation(.snappy(duration: 0.22)) { chromeVisible.toggle() }
                }

                if inputMode == .trackpad,
                   let size = controller.framebufferSize {
                    TrackpadCursorOverlay(
                        normalizedPosition: trackpadCursor,
                        framebufferSize: CGSize(width: CGFloat(size.width),
                                                height: CGFloat(size.height)),
                        isVisible: true
                    )
                }

                statusOverlay(for: controller.state, controller: controller)

                if chromeVisible {
                    overlayChrome(controller: controller)
                        .transition(.opacity.combined(with: .move(edge: .top)))
                }

                // Persistent pull-tab so the chrome is always reachable
                // (three-finger tap is the power-user shortcut).
                VStack {
                    chromeToggleHandle
                    Spacer()
                }
            } else {
                ProgressView("Preparing session…")
                    .tint(.white)
                    .foregroundStyle(.white)
            }
        }
        .navigationTitle(controller?.desktopName ?? connection.displayName)
        #if os(iOS)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar(.hidden, for: .navigationBar)
        .toolbar(.hidden, for: .tabBar)
        #endif
        .task(id: connection.id) {
            await startSession()
        }
        .onChange(of: scenePhase) { _, phase in
            if phase == .background {
                controller?.stop()
            }
        }
        .onDisappear {
            controller?.stop()
            persistLastConnected()
        }
        .sheet(item: $screenshotItem) { item in
            ShareSheet(items: [item.image])
        }
    }

    private var chromeToggleHandle: some View {
        Button {
            withAnimation(.snappy(duration: 0.22)) {
                chromeVisible.toggle()
            }
        } label: {
            Image(systemName: chromeVisible ? "chevron.up" : "chevron.down")
                .font(.caption.weight(.bold))
                .foregroundStyle(.white)
                .frame(width: 44, height: 20)
                .contentShape(Rectangle())
        }
        .buttonStyle(.plain)
        .glassSurface(in: Capsule())
        .accessibilityLabel(chromeVisible ? "Hide toolbar" : "Show toolbar")
        .padding(.top, chromeVisible ? 4 : 2)
    }

    @ViewBuilder
    private func overlayChrome(controller: SessionController) -> some View {
        VStack(spacing: 8) {
            HStack(spacing: 10) {
                Button {
                    stopAndDismiss()
                } label: {
                    Image(systemName: "chevron.left")
                        .font(.callout.weight(.semibold))
                        .frame(width: 34, height: 34)
                }
                .buttonStyle(.plain)
                .glassSurface(in: Circle())
                .accessibilityLabel("Back")

                connectionLabel(controller: controller)

                if controller.viewOnly {
                    HStack(spacing: 4) {
                        Image(systemName: "eye.fill")
                            .font(.caption2)
                        Text("View only")
                            .font(.caption.weight(.semibold))
                    }
                    .foregroundStyle(.yellow)
                    .padding(.horizontal, 10)
                    .padding(.vertical, 5)
                    .glassSurface(in: Capsule())
                }

                Spacer()
            }
            .padding(.horizontal, 12)
            .padding(.top, 4)

            SessionToolbar(
                controller: controller,
                inputMode: $inputMode,
                showKeyboardBar: $showSoftwareKeyboard,
                selectedScreenID: $selectedScreenID,
                onScreenshot: { takeScreenshot(controller: controller) },
                onDisconnect: { stopAndDismiss() }
            )

            Spacer()
        }
    }

    @ViewBuilder
    private func connectionLabel(controller: SessionController) -> some View {
        VStack(alignment: .leading, spacing: 0) {
            Text(controller.desktopName ?? connection.displayName)
                .font(.headline)
                .foregroundStyle(.white)
                .lineLimit(1)
            if let size = controller.framebufferSize {
                Text("\(size.width)×\(size.height)")
                    .font(.caption2.monospacedDigit())
                    .foregroundStyle(.white.opacity(0.6))
            }
        }
        .padding(.horizontal, 14)
        .padding(.vertical, 6)
        .glassSurface(in: Capsule())
    }

    @ViewBuilder
    private func statusOverlay(for state: SessionState,
                               controller: SessionController) -> some View {
        switch state {
        case .connecting:
            messageOverlay {
                ProgressView("Connecting…").tint(.white).foregroundStyle(.white)
            }
        case .authenticating:
            messageOverlay {
                ProgressView("Authenticating…").tint(.white).foregroundStyle(.white)
            }
        case .disconnected(let reason):
            disconnectedOverlay(reason: reason, controller: controller)
        case .idle, .connected:
            EmptyView()
        }
    }

    @ViewBuilder
    private func disconnectedOverlay(reason: DisconnectReason,
                                     controller: SessionController) -> some View {
        VStack(spacing: 14) {
            Image(systemName: glyph(for: reason))
                .font(.system(size: 36, weight: .semibold))
                .foregroundStyle(tint(for: reason))
            Text(headline(for: reason))
                .font(.headline)
                .foregroundStyle(.white)
            Text(humanReadable(reason: reason, lastError: controller.lastErrorMessage))
                .font(.callout)
                .foregroundStyle(.white.opacity(0.75))
                .multilineTextAlignment(.center)
                .padding(.horizontal, 24)
            HStack(spacing: 12) {
                Button {
                    controller.reconnectNow()
                } label: {
                    Label("Reconnect", systemImage: "arrow.clockwise")
                }
                .glassButton(prominent: true)
                Button("Close") {
                    stopAndDismiss()
                }
                .glassButton()
            }
            if controller.isReconnecting {
                HStack(spacing: 8) {
                    ProgressView().controlSize(.small)
                    Text("Retrying… attempt \(controller.reconnectAttempt)")
                        .font(.caption)
                        .foregroundStyle(.white.opacity(0.7))
                }
            }
        }
        .padding(28)
        .frame(maxWidth: 360)
        .glassSurface(in: RoundedRectangle(cornerRadius: 28, style: .continuous))
    }

    @ViewBuilder
    private func messageOverlay<V: View>(@ViewBuilder _ content: () -> V) -> some View {
        content()
            .padding(22)
            .glassSurface(in: RoundedRectangle(cornerRadius: 22, style: .continuous))
    }

    private func selectedScreen(for controller: SessionController) -> RemoteScreen? {
        guard let id = selectedScreenID else { return nil }
        return controller.screens.first { $0.id == id }
    }

    private func startSession() async {
        if controller == nil {
            inputMode = preferredInputMode()
            let provider = DefaultPasswordProvider()
            let controller = SessionController(connection: connection,
                                               passwordProvider: provider)
            self.controller = controller
            controller.start()
        }
    }

    private func preferredInputMode() -> InputMode {
        switch connection.inputMode {
        case .trackpad: return .trackpad
        case .touch: return InputMode(rawValue: defaultInputModeRaw) ?? .touch
        }
    }

    private func stopAndDismiss() {
        controller?.stop()
        persistLastConnected()
        dismiss()
    }

    private func persistLastConnected() {
        connection.lastConnectedAt = .now
        try? modelContext.save()
    }

    private func humanReadable(reason: DisconnectReason, lastError: String?) -> String {
        switch reason {
        case .userRequested: return "You ended this session."
        case .authenticationFailed: return "Authentication failed. Check the password and try again."
        case .networkError(let detail): return lastError ?? detail
        case .protocolError(let detail): return lastError ?? detail
        case .remoteClosed: return "The remote computer closed the session."
        }
    }

    private func glyph(for reason: DisconnectReason) -> String {
        switch reason {
        case .userRequested: "checkmark.circle"
        case .authenticationFailed: "lock.trianglebadge.exclamationmark"
        case .networkError, .remoteClosed: "wifi.exclamationmark"
        case .protocolError: "exclamationmark.triangle"
        }
    }

    private func tint(for reason: DisconnectReason) -> Color {
        switch reason {
        case .userRequested: .green
        case .authenticationFailed: .yellow
        default: .orange
        }
    }

    private func headline(for reason: DisconnectReason) -> String {
        switch reason {
        case .userRequested: "Session ended"
        case .authenticationFailed: "Authentication failed"
        default: "Disconnected"
        }
    }

    private func takeScreenshot(controller: SessionController) {
        #if canImport(UIKit)
        guard let cgImage = controller.currentImage else { return }
        let image = UIImage(cgImage: cgImage)
        screenshotItem = ScreenshotShareItem(image: image)
        #endif
    }
}

private struct ScreenshotShareItem: Identifiable {
    let id = UUID()
    #if canImport(UIKit)
    let image: UIImage
    #else
    let image: Any
    #endif
}

#if canImport(UIKit)
private struct ShareSheet: UIViewControllerRepresentable {
    let items: [Any]

    func makeUIViewController(context: Context) -> UIActivityViewController {
        UIActivityViewController(activityItems: items, applicationActivities: nil)
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}
#else
private struct ShareSheet: View {
    let items: [Any]
    var body: some View { EmptyView() }
}
#endif
