import SwiftUI
import AoqClientSdk

// MARK: - Video Understanding View

struct VideoUnderstandingView: View {
    @StateObject private var mgr = AoqEngineManager.shared

    // Render
    @State private var renderView: UIView?
    @State private var renderViewBound: Bool = false

    // Mode: true = camera live, false = released camera + background image
    @State private var cameraActive: Bool = true
    @State private var isFrontCamera: Bool = false

    // Chat panel state
    @State private var messages: [ChatMessage] = []
    @State private var panelExpanded: Bool = false
    @State private var dragTranslation: CGFloat = 0

    // Input state
    @State private var isVoiceMode: Bool = true
    @State private var inputText: String = ""
    @State private var isRecording: Bool = false
    @FocusState private var isInputFocused: Bool

    // 侧边栏 + 链接设置
    @State private var showSidePanel: Bool = false
    @State private var selectedSetting: VUSettingItem? = nil

    // 链接配置（AppServer 填充）
    @State private var token: String = ""
    @State private var sid: String = ""
    @State private var certificate: String = ""
    /* relay 接入点列表，由 AppServer 填充后传给 connect。 */
    @State private var relayEndpoints: [AoqRelayEndpoint] = []
    @State private var workspaceIdHash: String = ""

    // AppServer 请求参数 / 原始返回（占据原"获取配置"按钮位置）
    @State private var model: String = "qwen3.5-omni-plus-realtime"
    @State private var rawAppServerResponse: String = ""

    // 音频编码参数（默认：PCM / 16000 / 1ch / 24000bps，仅记录，不调 API）
    @State private var audioCodec: AoqEncoderType = .audioPCM
    @State private var audioSampleRate: Int = 16000
    @State private var audioChannel: Int = 1
    @State private var audioBitrate: Int = 24000

    // 音频解码参数
    @State private var audioDecodeCodec: AoqEncoderType = .audioPCM
    @State private var audioDecodeSampleRate: Int = 16000
    @State private var audioDecodeChannel: Int = 1
    @State private var audioDecodeBitrate: Int = 24000

    // 视频编码参数（默认：JPEG / 720x1280 / 2fps / 500kbps，仅记录，不调 API）
    @State private var videoCodec: AoqEncoderType = .videoJpeg
    @State private var videoWidth: String = "720"
    @State private var videoHeight: String = "1280"
    @State private var videoFps: String = "2"
    @State private var videoBitrate: String = "500000"
    @State private var videoMinBitrate: String = "128000"
    @State private var videoKeyframeInterval: String = "2"

    // 链接流程状态
    @State private var linkStatus: VULinkStatus = .idle
    @State private var linkStatusMessage: String = ""

    var body: some View {
        ZStack {
            // 第一层：摄像头渲染 / 背景图（始终全屏，不响应任何安全区与键盘）
            backgroundLayer
                .ignoresSafeArea()

            // 透明点击层：键盘聚焦时点击空白处释放焦点。
            // 放在背景之上、控制层之下，按钮 / 输入框等前景交互优先级更高，不受影响。
            if isInputFocused {
                Color.clear
                    .contentShape(Rectangle())
                    .ignoresSafeArea()
                    .onTapGesture { isInputFocused = false }
            }

            // 第二层：顶部控制栏（面包栏 + 喇叭按钮）
            // 固定贴顶部安全区，不会因键盘弹出而上移。
            VStack(spacing: 0) {
                topBar
                Spacer(minLength: 0)
            }
            .ignoresSafeArea(.keyboard, edges: .bottom)

            // 第三层：底部输入框 + 半屏展示内容区
            // 响应键盘安全区，键盘弹出时整体上推。
            VStack(spacing: 0) {
                Spacer(minLength: 0)
                bottomComposite
            }

            // 第四层：侧边栏（最顶层）。仅黑色遮罩需要 ignoresSafeArea，
            // 面板本身保留安全区，避免被推到状态栏下面。
            if showSidePanel {
                sidePanelOverlay
                    .zIndex(10)
            }
        }
        .navigationBarHidden(true)
        .background(VUSwipeBackEnabler())
        .onAppear {
            setupCamera()
        }
        .onDisappear { teardown() }
        .onReceive(mgr.incomingDataMsg) { msg in
            messages.append(ChatMessage(sender: .ai, content: msg.text, time: Date()))
            withAnimation(.easeInOut(duration: 0.25)) { panelExpanded = true }
        }
        .onReceive(mgr.outgoingDataMsg) { msg in
            messages.append(ChatMessage(sender: .user, content: msg.text, time: Date()))
        }
        .onReceive(mgr.$isConnected) { connected in
            if connected && linkStatus == .connecting {
                linkStatus = .connected
                linkStatusMessage = "链接成功"
                mgr.startAudioPlayer(channel: 1, isExternal: false)
                mgr.isSpeakerOn = true
            } else if !connected && linkStatus == .connected {
                linkStatus = .idle
                linkStatusMessage = "已断开"
            }
        }
    }

    // MARK: - Background

    @ViewBuilder
    private var backgroundLayer: some View {
        // Color.black 作为 layout 锚：始终接受 proposed size 并不向上汇报固有尺寸。
        // 否则 Image+aspectRatio(.fill) 会按素材比例把 ZStack 撑出屏幕，
        // 导致顶/底栏被推出可视区、摄像头比例失真。
        Color.black
            .overlay(
                // 容器底层：展位图，常驻。摄像头销毁后自动透出。
                Image("Background")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
            )
            .overlay(
                // 容器上层：摄像头渲染视图。仅在 cameraActive 时挂载，自然遮挡展位图；
                // cameraActive=false 时此视图从层级移除并销毁。
                Group {
                    if cameraActive {
                        VURenderRepresentable { view in
                            self.renderView = view
                            self.bindRenderViewIfReady()
                        }
                    }
                }
            )
            .clipped()
    }

    // MARK: - Top Bar

    private var topBar: some View {
        HStack {
            circleIconButton(systemName: "line.3.horizontal") {
                withAnimation(.easeInOut(duration: 0.25)) { showSidePanel.toggle() }
            }
            Spacer()
            HStack(spacing: 5) {
                circleIconButton(systemName: "camera.rotate") {
                    guard cameraActive else { return }
                    isFrontCamera.toggle()
                    _ = mgr.switchCamera(direction: isFrontCamera ? .front : .back)
                }
                .opacity(cameraActive ? 1.0 : 0.4)
                circleIconButton(systemName: mgr.isSpeakerOn ? "speaker.wave.2.fill" : "speaker.slash.fill") {
                    mgr.isSpeakerOn.toggle()
                    if mgr.isSpeakerOn {
                        mgr.startAudioPlayer(channel: 1, isExternal: false)
                    } else {
                        mgr.stopAudioPlayer()
                    }
                }
            }
        }
        .padding(.horizontal, 16)
        .padding(.top, 8)
    }

    private func circleIconButton(systemName: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            Image(systemName: systemName)
                .font(.body.bold())
                .foregroundStyle(.white)
                .frame(width: 40, height: 40)
                .background(Color.black.opacity(0.55))
                .clipShape(Circle())
        }
    }

    // MARK: - Detection Bubble

//    private var detectionBubble: some View {
//        HStack(spacing: 6) {
//            Image(systemName: "viewfinder")
//                .font(.caption)
//                .foregroundStyle(.green)
//            Text("检测到办公桌面场景")
//                .font(.subheadline)
//                .foregroundStyle(.white)
//        }
//        .padding(.horizontal, 14)
//        .padding(.vertical, 8)
//        .background(Color.black.opacity(0.55))
//        .clipShape(Capsule())
//    }

    // MARK: - Bottom Composite

    private var bottomComposite: some View {
        VStack(spacing: 0) {
            if panelExpanded {
                chatPanel
                    .transition(.move(edge: .bottom).combined(with: .opacity))
            } else {
                collapsedDragArea
            }
            inputBar
                .padding(.horizontal, 12)
                .padding(.bottom, 8)
        }
    }

    // 折叠态：仅展示拖动条，固定在 inputBar 上方。
    private var collapsedDragArea: some View {
        dragHandleCapsule
            .padding(.vertical, 10)
            .frame(maxWidth: .infinity)
            .contentShape(Rectangle())
            .gesture(panelDragGesture)
            .onTapGesture {
                withAnimation(.easeInOut(duration: 0.25)) { panelExpanded.toggle() }
            }
    }

    // MARK: - Chat Panel

    private var chatPanel: some View {
        VStack(spacing: 0) {
            chatPanelTopRow
            chatMessages
        }
        .frame(maxWidth: .infinity)
        .frame(height: UIScreen.main.bounds.height * 0.5)
        .background(Color.black.opacity(0.45))
        .clipShape(UnevenRoundedRectangle(topLeadingRadius: 18, topTrailingRadius: 18))
        // 点击半屏面板任何区域 → 释放输入焦点收键盘；
        // simultaneousGesture 与 ScrollView 滚动手势可共存。
        .simultaneousGesture(
            TapGesture()
                .onEnded { if isInputFocused { isInputFocused = false } }
        )
    }

    // 展开态的顶部：中间拖动条（可拖动收起） + 右上角关闭按钮。
    // 现在拖动条直接嵌在面板内部，会随面板一起移动到半屏顶部。
    private var chatPanelTopRow: some View {
        ZStack {
            dragHandleCapsule
                .frame(width: 80, height: 28)
                .contentShape(Rectangle())
                .gesture(panelDragGesture)
                .onTapGesture {
                    withAnimation(.easeInOut(duration: 0.25)) { panelExpanded.toggle() }
                }

            HStack {
                Spacer()
                Button {
                    withAnimation(.easeInOut(duration: 0.25)) { panelExpanded = false }
                    isInputFocused = false
                } label: {
                    Image(systemName: "xmark")
                        .font(.body.bold())
                        .foregroundStyle(.white.opacity(0.85))
                        .frame(width: 32, height: 32)
                }
            }
        }
        .padding(.horizontal, 8)
        .padding(.top, 6)
        .padding(.bottom, 2)
    }

    private var dragHandleCapsule: some View {
        Capsule()
            .fill(panelExpanded ? Color.white.opacity(0.55) : Color.white.opacity(0.75))
            .frame(width: 40, height: 4)
    }

    private var panelDragGesture: some Gesture {
        DragGesture(minimumDistance: 8)
            .onChanged { value in
                dragTranslation = value.translation.height
            }
            .onEnded { value in
                let dy = value.translation.height
                withAnimation(.easeInOut(duration: 0.25)) {
                    if dy < -30 {
                        panelExpanded = true
                    } else if dy > 30 {
                        panelExpanded = false
                    }
                }
                dragTranslation = 0
            }
    }

    private var chatMessages: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 14) {
                    ForEach(messages) { msg in
                        chatRow(msg).id(msg.id)
                    }
                }
                .padding(.horizontal, 16)
                .padding(.bottom, 12)
            }
            .scrollIndicators(.hidden)
            .onChange(of: messages.count) { _ in
                if let last = messages.last {
                    withAnimation { proxy.scrollTo(last.id, anchor: .bottom) }
                }
            }
        }
    }

    @ViewBuilder
    private func chatRow(_ msg: ChatMessage) -> some View {
        HStack(alignment: .top) {
            if msg.sender == .user {
                Spacer(minLength: 40)
                Text(msg.content)
                    .font(.body)
                    .foregroundStyle(.white)
                    .padding(.horizontal, 14)
                    .padding(.vertical, 9)
                    .background(Color.white.opacity(0.18))
                    .clipShape(RoundedRectangle(cornerRadius: 16))
            } else {
                Text(msg.content)
                    .font(.body)
                    .foregroundStyle(.white)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
        }
    }

    // MARK: - Input Bar

    private var inputBar: some View {
        HStack(spacing: 8) {
            // 单按钮在 map / camera 之间切换：
            //   cameraActive=true  → map 图标，点击后销毁摄像头渲染，露出展位图
            //   cameraActive=false → camera 图标，点击后重新挂上渲染视图，遮住展位图
            inputSideButton(systemName: cameraActive ? "map" : "camera") {
                if cameraActive {
                    switchToBackgroundMode()
                } else {
                    switchToCameraMode()
                }
            }

            if isVoiceMode {
                pushToTalkButton
            } else {
                textInputField
            }

            inputSideButton(systemName: isVoiceMode ? "keyboard" : "waveform") {
                isVoiceMode.toggle()
                if !isVoiceMode {
                    withAnimation(.easeInOut(duration: 0.25)) { panelExpanded = true }
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                        isInputFocused = true
                    }
                } else {
                    isInputFocused = false
                }
            }
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 8)
        .background(.ultraThinMaterial)
        .clipShape(RoundedRectangle(cornerRadius: 26))
        .overlay(
            RoundedRectangle(cornerRadius: 26)
                .stroke(Color.white.opacity(0.15), lineWidth: 0.5)
        )
        .shadow(color: .black.opacity(0.15), radius: 8, y: 2)
    }

    private func inputSideButton(systemName: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            Image(systemName: systemName)
                .font(.title3)
                .foregroundStyle(.primary)
                .frame(width: 36, height: 36)
        }
    }

    private var pushToTalkButton: some View {
        Text(isRecording ? "松开 结束" : "按住说话")
            .font(.body.bold())
            .foregroundStyle(isRecording ? .red : .primary)
            .frame(maxWidth: .infinity)
            .frame(height: 36)
            .scaleEffect(isRecording ? 1.02 : 1.0)
            .animation(.easeInOut(duration: 0.15), value: isRecording)
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged { _ in if !isRecording { beginRecording() } }
                    .onEnded { _ in endRecording() }
            )
    }

    private var textInputField: some View {
        HStack(spacing: 6) {
            TextField("文本输入", text: $inputText)
                .font(.subheadline)
                .focused($isInputFocused)
                .submitLabel(.send)
                .onSubmit { sendText() }
                .padding(.leading, 8)

            Button { sendText() } label: {
                Image(systemName: "arrow.up")
                    .font(.footnote.bold())
                    .foregroundStyle(.white)
                    .frame(width: 28, height: 28)
                    .background(inputText.isEmpty ? Color.gray.opacity(0.4) : Color.primary)
                    .clipShape(Circle())
            }
            .disabled(inputText.isEmpty)
        }
        .padding(.vertical, 4)
        .padding(.trailing, 4)
        .frame(maxWidth: .infinity)
        .background(Color(uiColor: .systemBackground).opacity(0.6))
        .clipShape(Capsule())
    }

    // MARK: - Actions

    private func sendText() {
        let t = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !t.isEmpty else { return }
        inputText = ""
        withAnimation(.easeInOut(duration: 0.25)) { panelExpanded = true }
        if mgr.isConnected {
            mgr.sendDataMsg(text: t)
        }
    }

    private func beginRecording() {
        isRecording = true
        if mgr.isConnected {
            mgr.interruptAudioPlayer()
            mgr.startAudioCapture(channel: 1, isExternal: false, isVoipMode: false)
        }
    }

    private func endRecording() {
        isRecording = false
        if mgr.isConnected {
            mgr.stopAudioCapture()
        }
    }

    // MARK: - Camera lifecycle

    private func setupCamera() {
        if !mgr.isEngineCreated {
            mgr.createEngine(workDir: NSTemporaryDirectory(), enableDumpAudio: false, extras: "")
        }
        startCapture()
        mgr.startAudioPlayer(channel: 1, isExternal: false)
        mgr.isSpeakerOn = true
    }

    private func teardown() {
        if renderViewBound {
            mgr.setLocalView(nil, trackType: .video)
            renderViewBound = false
        }
        if mgr.isConnected {
            mgr.stopAudioCapture()
            mgr.stopAudioPlayer()
            mgr.stopVideoCapture()
            mgr.disconnect()
        } else if mgr.isEngineCreated {
            mgr.stopVideoCapture()
        }
        if mgr.isEngineCreated {
            mgr.destroyEngine()
        }
    }

    private func startCapture() {
        guard mgr.isEngineCreated else { return }
        mgr.startVideoCapture(isExternal: false, cameraDirection: .back)
        bindRenderViewIfReady()
    }

    private func bindRenderViewIfReady() {
        guard cameraActive,
              !renderViewBound,
              let view = renderView,
              mgr.isEngineCreated else { return }
        mgr.setLocalView(view, trackType: .video, renderMode: .fill)
        renderViewBound = true
    }

    private func switchToBackgroundMode() {
        if renderViewBound {
            mgr.setLocalView(nil, trackType: .video)
            renderViewBound = false
        }
        if mgr.isEngineCreated {
            mgr.stopVideoCapture()
        }
        // 清空持有的 UIView 引用，让 SwiftUI 真正销毁 representable 内的 UIView。
        renderView = nil
        withAnimation(.easeInOut(duration: 0.25)) { cameraActive = false }
    }

    private func switchToCameraMode() {
        withAnimation(.easeInOut(duration: 0.25)) { cameraActive = true }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            startCapture()
        }
    }

    // MARK: - Side Panel

    private var sidePanelOverlay: some View {
        ZStack(alignment: .leading) {
            Color.black.opacity(0.3)
                .onTapGesture {
                    withAnimation(.easeInOut(duration: 0.25)) {
                        showSidePanel = false
                        selectedSetting = nil
                    }
                }
            sidePanelContent
                .frame(width: UIScreen.main.bounds.width * 0.78)
                .background(Color(uiColor: .systemBackground))
                .transition(.move(edge: .leading))
        }
    }

    private var sidePanelContent: some View {
        VStack(alignment: .leading, spacing: 0) {
            sidePanelHeader
            Divider()
            if let selected = selectedSetting {
                settingDetail(selected)
            } else {
                settingList
            }
        }
    }

    private var sidePanelHeader: some View {
        HStack {
            if selectedSetting != nil {
                Button {
                    withAnimation(.easeInOut(duration: 0.2)) { selectedSetting = nil }
                } label: {
                    Image(systemName: "chevron.left").font(.body.bold())
                }
            }
            Text(selectedSetting?.title ?? "设置")
                .font(.title3.bold())
            Spacer()
            Button {
                withAnimation(.easeInOut(duration: 0.25)) {
                    showSidePanel = false
                    selectedSetting = nil
                }
            } label: {
                Image(systemName: "xmark.circle.fill")
                    .font(.title3)
                    .foregroundStyle(.secondary)
            }
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 14)
    }

    private var settingList: some View {
        VStack(spacing: 0) {
            ForEach(VUSettingItem.allCases, id: \.self) { item in
                Button {
                    withAnimation(.easeInOut(duration: 0.2)) { selectedSetting = item }
                } label: {
                    HStack {
                        Image(systemName: item.icon)
                            .frame(width: 24)
                            .foregroundStyle(.blue)
                        Text(item.title)
                            .font(.body)
                            .foregroundStyle(.primary)
                        Spacer()
                        Image(systemName: "chevron.right")
                            .font(.caption)
                            .foregroundStyle(.tertiary)
                    }
                    .padding(.horizontal, 16)
                    .padding(.vertical, 14)
                }
                Divider().padding(.leading, 56)
            }
            Spacer()
        }
    }

    @ViewBuilder
    private func settingDetail(_ item: VUSettingItem) -> some View {
        ScrollView {
            VStack(spacing: 12) {
                switch item {
                case .connection: connectionSettings
                case .audioCodec: audioCodecSettings
                case .videoCodec: videoCodecSettings
                case .ui:         uiSettings
                }
            }
            .padding(16)
        }
    }

    private var connectionSettings: some View {
        VStack(alignment: .leading, spacing: 12) {
            // —— AppServer 配置展示区 ——
            Text("AppServer 配置")
                .font(.subheadline.bold())

            HStack {
                Text("URL")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                Text(AppServerConfig.currentAllocateURL)
                    .font(.caption)
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
                    .truncationMode(.middle)
                    .textSelection(.enabled)
            }

            VStack(alignment: .leading, spacing: 4) {
                Text("Model").font(.caption).foregroundStyle(.secondary)
                Text(model)
                    .font(.subheadline)
                    .foregroundStyle(.primary)
            }

            // 原始返回展示框：占据之前"获取配置"按钮的位置。
            // 链接按钮触发后填充。
            VStack(alignment: .leading, spacing: 4) {
                Text("AppServer 原始返回")
                    .font(.caption)
                    .foregroundStyle(.secondary)
                ScrollView {
                    Text(rawAppServerResponse.isEmpty ? "(尚未获取)" : rawAppServerResponse)
                        .font(.system(size: 11, design: .monospaced))
                        .foregroundStyle(rawAppServerResponse.isEmpty ? .tertiary : .primary)
                        .textSelection(.enabled)
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding(8)
                }
                .frame(height: 140)
                .background(Color(uiColor: .systemGray6))
                .clipShape(RoundedRectangle(cornerRadius: 8))
            }

            Divider()

            // 一键链接按钮：allocate → createEngine → connect
            Button {
                Task { await performLink() }
            } label: {
                HStack {
                    if linkStatus == .connecting || linkStatus == .allocating {
                        ProgressView().controlSize(.small)
                    }
                    Text(linkStatus == .connected ? "重新链接" : "链接")
                        .frame(maxWidth: .infinity)
                }
            }
            .buttonStyle(.borderedProminent)
            .disabled(linkStatus == .connecting || linkStatus == .allocating || model.isEmpty)

            // 状态展示：在链接按钮下方
            if !linkStatusMessage.isEmpty {
                HStack(spacing: 6) {
                    Circle()
                        .fill(linkStatusColor)
                        .frame(width: 8, height: 8)
                    Text(linkStatusMessage)
                        .font(.caption)
                        .foregroundStyle(linkStatusColor)
                        .lineLimit(3)
                }
            }
        }
        .padding(12)
        .background(Color(uiColor: .secondarySystemGroupedBackground))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    private var linkStatusColor: Color {
        switch linkStatus {
        case .idle:                    return .secondary
        case .allocating, .connecting: return .orange
        case .connected:               return .green
        case .failed:                  return .red
        }
    }

    private var audioCodecSettings: some View {
        VStack(spacing: 12) {
            VStack(alignment: .leading, spacing: 8) {
                Text("音频编码参数").font(.subheadline.bold())
                HStack {
                    Text("编码").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioCodec) {
                        Text("PCM").tag(AoqEncoderType.audioPCM)
                        Text("Opus").tag(AoqEncoderType.audioOpus)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("采样率").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioSampleRate) {
                        Text("8000").tag(8000)
                        Text("16000").tag(16000)
                        Text("48000").tag(48000)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("声道数").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioChannel) {
                        Text("1").tag(1)
                        Text("2").tag(2)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("码率").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioBitrate) {
                        Text("12000").tag(12000)
                        Text("24000").tag(24000)
                        Text("48000").tag(48000)
                        Text("64000").tag(64000)
                    }
                    .pickerStyle(.segmented)
                }
            }
            .padding(12)
            .background(Color(uiColor: .secondarySystemGroupedBackground))
            .clipShape(RoundedRectangle(cornerRadius: 10))

            VStack(alignment: .leading, spacing: 8) {
                Text("音频解码参数").font(.subheadline.bold())
                HStack {
                    Text("编码").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioDecodeCodec) {
                        Text("PCM").tag(AoqEncoderType.audioPCM)
                        Text("Opus").tag(AoqEncoderType.audioOpus)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("采样率").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioDecodeSampleRate) {
                        Text("8000").tag(8000)
                        Text("16000").tag(16000)
                        Text("48000").tag(48000)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("声道数").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioDecodeChannel) {
                        Text("1").tag(1)
                        Text("2").tag(2)
                    }
                    .pickerStyle(.segmented)
                }
                HStack {
                    Text("码率").frame(width: 60, alignment: .leading).foregroundStyle(.secondary)
                    Picker("", selection: $audioDecodeBitrate) {
                        Text("12000").tag(12000)
                        Text("24000").tag(24000)
                        Text("48000").tag(48000)
                        Text("64000").tag(64000)
                    }
                    .pickerStyle(.segmented)
                }
            }
            .padding(12)
            .background(Color(uiColor: .secondarySystemGroupedBackground))
            .clipShape(RoundedRectangle(cornerRadius: 10))

            Text("参数仅记录，在点击链接按钮时应用。")
                .font(.caption2)
                .foregroundStyle(.tertiary)
        }
    }

    // 视频编码：仅记录参数，链接时由 performLink 应用到 mgr.setVideoEncoderConfig。
    private var videoCodecSettings: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("视频编码参数")
                .font(.subheadline.bold())

            HStack {
                Text("编码").frame(width: 80, alignment: .leading).foregroundStyle(.secondary)
                Picker("", selection: $videoCodec) {
                    Text("H264").tag(AoqEncoderType.videoH264)
                    Text("JPEG").tag(AoqEncoderType.videoJpeg)
                }
                .pickerStyle(.segmented)
            }

            HStack {
                Text("分辨率预设").frame(width: 80, alignment: .leading).foregroundStyle(.secondary)
                Menu {
                    Button("720×1280")  { videoWidth = "720";  videoHeight = "1280" }
                    Button("1280×720")  { videoWidth = "1280"; videoHeight = "720"  }
                    Button("960×540")   { videoWidth = "960";  videoHeight = "540"  }
                    Button("640×480")   { videoWidth = "640";  videoHeight = "480"  }
                    Button("320×240")   { videoWidth = "320";  videoHeight = "240"  }
                } label: {
                    Text("\(videoWidth)×\(videoHeight)")
                }
                Spacer()
            }

            HStack {
                vuNumberField(label: "宽(px)", text: $videoWidth)
                vuNumberField(label: "高(px)", text: $videoHeight)
            }
            HStack {
                vuNumberField(label: "fps", text: $videoFps)
                vuNumberField(label: "关键帧(秒)", text: $videoKeyframeInterval)
            }
            HStack {
                vuNumberField(label: "目标码率(bps)", text: $videoBitrate)
                vuNumberField(label: "最小码率(bps)", text: $videoMinBitrate)
            }

            Text("参数仅记录，在点击链接按钮时应用。")
                .font(.caption2)
                .foregroundStyle(.tertiary)
        }
        .padding(12)
        .background(Color(uiColor: .secondarySystemGroupedBackground))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    @ViewBuilder
    private func vuNumberField(label: String, text: Binding<String>) -> some View {
        HStack(spacing: 4) {
            Text(label).foregroundStyle(.secondary).font(.caption)
            TextField("", text: text)
                .textFieldStyle(.roundedBorder)
                .keyboardType(.numberPad)
                .frame(minWidth: 60)
        }
    }

    private var uiSettings: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack {
                Text("SDK 回调日志").font(.subheadline).bold()
                Spacer()
                Button("清空") { mgr.clearLog() }.font(.caption)
            }
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 4) {
                    ForEach(mgr.events) { entry in
                        Text("[\(entry.timestamp)] \(entry.message)")
                            .font(.system(size: 11, design: .monospaced))
                            .foregroundStyle(.primary)
                    }
                }
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(8)
            }
            .frame(maxHeight: 360)
            .background(Color(uiColor: .systemGray6))
            .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .padding(12)
        .background(Color(uiColor: .secondarySystemGroupedBackground))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    // MARK: - Link Flow

    private func performLink() async {
        // 1. Allocate appserver（直接发请求，便于同时拿到原始 JSON 字符串展示在面板上）
        linkStatus = .allocating
        linkStatusMessage = "请求 AppServer..."
        rawAppServerResponse = ""

        let fullURL = AppServerConfig.allocateURL(from: AppServerConfig.currentBaseURL)
        guard let url = URL(string: fullURL) else {
            linkStatus = .failed
            linkStatusMessage = "URL 无效: \(fullURL)"
            return
        }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: ["model": model])

        let data: Data
        let response: URLResponse
        do {
            (data, response) = try await URLSession.shared.data(for: request)
        } catch {
            linkStatus = .failed
            linkStatusMessage = "网络错误: \(error.localizedDescription)"
            return
        }

        // 展示原始返回（无论成功失败都展示，方便排查）
        rawAppServerResponse = prettyPrintJSON(data) ?? (String(data: data, encoding: .utf8) ?? "(无返回内容)")

        if let httpResp = response as? HTTPURLResponse, !(200..<300).contains(httpResp.statusCode) {
            linkStatus = .failed
            linkStatusMessage = "HTTP \(httpResp.statusCode)"
            return
        }

        let resp: AllocateResponse
        do {
            resp = try JSONDecoder().decode(AllocateResponse.self, from: data)
        } catch {
            linkStatus = .failed
            linkStatusMessage = "解析失败: \(error.localizedDescription)"
            return
        }

        token = resp.aoqTokenForClient
        sid = resp.sid
        certificate = resp.clientRelayCertFingerprint
        relayEndpoints = resp.clientRelayEndpoints.map { item in
            let ep = AoqRelayEndpoint()
            ep.endpoint = item.endpoint
            ep.port = item.port
            return ep
        }
        workspaceIdHash = resp.extraInfo?.workspaceIdHash ?? ""

        // 2. Ensure engine
        if !mgr.isEngineCreated {
            mgr.createEngine(workDir: NSTemporaryDirectory(), enableDumpAudio: false, extras: "")
        }

        // 3. 应用音频编解码配置
        let encCfg = AoqAudioCodecConfig()
        encCfg.codecType = audioCodec
        encCfg.sampleRate = audioSampleRate
        encCfg.channel = audioChannel
        encCfg.bitrate = audioBitrate
        mgr.setAudioEncoderConfig(encCfg)
        let decCfg = AoqAudioCodecConfig()
        decCfg.codecType = audioDecodeCodec
        decCfg.sampleRate = audioDecodeSampleRate
        decCfg.channel = audioDecodeChannel
        decCfg.bitrate = audioDecodeBitrate
        mgr.setAudioDecoderConfig(decCfg)

        // 4. 应用视频编码配置
        let videoCfg = AoqVideoCodecConfig()
        videoCfg.trackType = .video
        videoCfg.codecType = videoCodec
        videoCfg.width = Int(videoWidth) ?? 720
        videoCfg.height = Int(videoHeight) ?? 1280
        videoCfg.fps = Int(videoFps) ?? 2
        videoCfg.bitrate = Int(videoBitrate) ?? 500000
        videoCfg.minBitrate = Int(videoMinBitrate) ?? 128000
        videoCfg.keyframeInterval = Int(videoKeyframeInterval) ?? 2
        mgr.setVideoEncoderConfig(videoCfg)

        // 5. Connect（成功结果通过 mgr.$isConnected 异步回调更新 linkStatus）
        linkStatus = .connecting
        linkStatusMessage = "正在连接..."
        let audioTrack = AoqTrackParam()
        audioTrack.trackType = .audio
        let videoTrack = AoqTrackParam()
        videoTrack.trackType = .video
        let dataTrack = AoqTrackParam()
        dataTrack.trackType = .data
        mgr.connect(
            token: token, sid: sid, certificate: certificate,
            relayEndpoints: relayEndpoints,
            workspaceIdHash: workspaceIdHash,
            publishTracks: [audioTrack, videoTrack, dataTrack],
            subscribeTracks: [audioTrack, dataTrack]
        )
    }

    /// 将 JSON 数据格式化为缩进字符串便于阅读，失败时返回 nil。
    private func prettyPrintJSON(_ data: Data) -> String? {
        guard let obj = try? JSONSerialization.jsonObject(with: data),
              let pretty = try? JSONSerialization.data(withJSONObject: obj,
                                                       options: [.prettyPrinted, .sortedKeys]) else {
            return nil
        }
        return String(data: pretty, encoding: .utf8)
    }
}

// MARK: - Setting Item / Link Status enums

enum VUSettingItem: String, CaseIterable {
    case audioCodec
    case videoCodec
    case connection
    case ui

    var title: String {
        switch self {
        case .connection: return "链接设置"
        case .audioCodec: return "音频编解码"
        case .videoCodec: return "视频编码"
        case .ui:         return "UI 设置"
        }
    }

    var icon: String {
        switch self {
        case .connection: return "link"
        case .audioCodec: return "waveform"
        case .videoCodec: return "video"
        case .ui:         return "eye"
        }
    }
}

enum VULinkStatus {
    case idle
    case allocating
    case connecting
    case connected
    case failed
}

// MARK: - UIView Bridge for SDK Render Target

private struct VURenderRepresentable: UIViewRepresentable {
    var onReady: (UIView) -> Void

    func makeUIView(context: Context) -> UIView {
        let v = UIView()
        v.backgroundColor = .black
        // 关闭触摸捕获，便于上层 tap-to-dismiss-keyboard 能命中此处。
        v.isUserInteractionEnabled = false
        DispatchQueue.main.async { onReady(v) }
        return v
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

// MARK: - Swipe Back Enabler

private struct VUSwipeBackEnabler: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        SwipeBackController()
    }
    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}

    private class SwipeBackController: UIViewController {
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            navigationController?.interactivePopGestureRecognizer?.isEnabled = true
            navigationController?.interactivePopGestureRecognizer?.delegate = nil
        }
    }
}

#Preview {
    NavigationStack { VideoUnderstandingView() }
}
