import SwiftUI
import AoqClientSdk

// MARK: - Phone Call View
// 全屏实时音视频通话界面：
//   背景为本地摄像头画面，底部为半屏可拖动聊天面板 + 通话控制按钮。
//   左上设置齿轮打开侧边栏（链接设置 / 音频编解码 / 视频编码 / UI 设置）。
//   未连接时显示居中拨打按钮，连接后显示通话控制栏。

struct PhoneCallView: View {
    @StateObject private var mgr = AoqEngineManager.shared
    @Environment(\.dismiss) private var dismiss

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

    // 摄像头开关 / 麦克风开关
    @State private var cameraActive: Bool = true
    @State private var isFrontCamera: Bool = false
    @State private var isMicOn: Bool = true
    @State private var pulseAnim: Bool = false

    // 字幕
    @State private var messages: [ChatMessage] = []
    @State private var showSubtitle: Bool = false
    @State private var showRawMessage: Bool = false

    // 输入栏（暂时隐藏，保留代码）
    @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 = ""

    @State private var model: String = "qwen3.5-omni-plus-realtime"
    @State private var rawAppServerResponse: String = ""
    @State private var selectedVoice: String = SessionConfig.currentVoice

    // 链接流程状态
    @State private var linkStatus: VULinkStatus = .idle
    @State private var linkStatusMessage: String = ""
    @State private var connectionStartTime: Date?
    @State private var connectionLatencyMs: Int?
    @State private var conversationState: ConversationState = .idle

    // 音频编码参数
    @State private var audioCodec: AoqEncoderType = .audioOpus
    @State private var audioSampleRate: Int = 16000
    @State private var audioChannel: Int = 1
    @State private var audioBitrate: Int = 24000

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

    // 视频编码参数
    @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"

    var body: some View {
        ZStack {
            // 全屏摄像头画面 / 背景
            backgroundLayer
                .ignoresSafeArea()

            // 相机ON + 字幕ON：浮动字幕框
            if cameraActive && showSubtitle {
                cameraSubtitleOverlay
                    .zIndex(3)
            }

            // 主布局：topBar + 字幕 + bottomControls 同一 VStack
            VStack(spacing: 0) {
                topBar
                if !cameraActive && showSubtitle {
                    chatMessages
                        .padding(.horizontal, 20)
                        .padding(.vertical, 16)
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(.ultraThinMaterial)
                } else {
                    Spacer(minLength: 0)
                }
                bottomControls
            }

            // 侧边栏
            if showSidePanel {
                sidePanelOverlay
                    .zIndex(10)
            }
        }
        .navigationBarHidden(true)
        .background(PCSwipeBackEnabler())
        .onAppear { setup() }
        .onDisappear { teardown() }
        .onReceive(mgr.incomingDataMsg) { msg in
            if showRawMessage {
                messages.append(ChatMessage(sender: .ai, content: msg.text, time: Date()))
            } else {
                handleParsedIncomingMessage(msg.text)
            }
        }
        .onReceive(mgr.outgoingDataMsg) { msg in
            if showRawMessage {
                messages.append(ChatMessage(sender: .user, content: msg.text, time: Date()))
            }
        }
        .onReceive(mgr.$isConnected) { connected in
            if connected && linkStatus == .connecting {
                linkStatus = .connected
                linkStatusMessage = "通话已接通"
                if let startTime = connectionStartTime {
                    connectionLatencyMs = Int(Date().timeIntervalSince(startTime) * 1000)
                }
                mgr.startAudioPlayer(channel: 1, isExternal: false)
                mgr.isSpeakerOn = true
                if isMicOn {
                    mgr.startAudioCapture(channel: 1, isExternal: false, isVoipMode: true)
                }
            } else if !connected && linkStatus == .connected {
                linkStatus = .idle
                linkStatusMessage = "通话已断开"
                conversationState = .idle
            }
        }
        .onReceive(mgr.conversationStateChanged) { state in
            conversationState = state
        }
    }

    // MARK: - Background

    @ViewBuilder
    private var backgroundLayer: some View {
        GeometryReader { geo in
            ZStack {
                Color(uiColor: .systemGroupedBackground)
                if cameraActive {
                    PCRenderRepresentable { view in
                        self.renderView = view
                        self.bindRenderViewIfReady()
                    }
                    .frame(width: geo.size.width, height: geo.size.height)
                } else {
                    voiceModeAvatar
                }
            }
            .clipped()
        }
        .ignoresSafeArea()
    }

    private var voiceModeAvatar: some View {
        ZStack {
            Circle()
                .fill(Color.blue.opacity(0.1))
                .frame(width: 80, height: 80)
            Image(systemName: "waveform")
                .font(.system(size: 34, weight: .medium))
                .foregroundStyle(.blue.opacity(0.6))
            Circle()
                .stroke(Color.blue.opacity(0.15), lineWidth: 1.5)
                .frame(width: 108, height: 108)
                .scaleEffect(pulseAnim ? 1.12 : 1.0)
                .opacity(pulseAnim ? 0 : 0.35)
        }
        .offset(y: -20)
        .onAppear {
            withAnimation(.easeInOut(duration: 2.2).repeatForever(autoreverses: true)) {
                pulseAnim = true
            }
        }
    }

    // MARK: - Top Bar

    private var topBar: some View {
        ZStack {
            // 左：设置
            HStack {
                circleIconButton(systemName: "gearshape.fill") {
                    withAnimation(.easeInOut(duration: 0.25)) { showSidePanel.toggle() }
                }
                Spacer()
            }

            // 右：前后置切换 + 扬声器 + 字幕
            HStack(spacing: 12) {
                Spacer()
                if cameraActive {
                    circleIconButton(systemName: "camera.rotate.fill") {
                        UIImpactFeedbackGenerator(style: .light).impactOccurred()
                        isFrontCamera.toggle()
                        _ = mgr.switchCamera(direction: isFrontCamera ? .front : .back)
                    }
                }
                circleIconButton(systemName: mgr.isSpeakerOn ? "speaker.wave.2.fill" : "speaker.slash.fill") {
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    toggleSpeaker()
                }
                circleIconButton(systemName: showSubtitle ? "captions.bubble.fill" : "captions.bubble") {
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    withAnimation(.easeInOut(duration: 0.25)) {
                        showSubtitle.toggle()
                    }
                }
            }
        }
        .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(cameraActive ? .white : Color(uiColor: .label))
                .frame(width: 40, height: 40)
        }
        .buttonStyle(.plain)
    }

    // MARK: - Call Entry Overlay

    private var callEntryOverlay: some View {
        VStack(spacing: 16) {
            Spacer()

            if linkStatus == .allocating || linkStatus == .connecting {
                ProgressView()
                    .scaleEffect(1.3)
                    .tint(.accentColor)
                Text(linkStatusMessage)
                    .font(.subheadline)
                    .foregroundStyle(.primary)
            } else {
                Button {
                    UIImpactFeedbackGenerator(style: .medium).impactOccurred()
                    Task { await performLink() }
                } label: {
                    Image(systemName: "phone.fill")
                        .font(.system(size: 28, weight: .semibold))
                        .foregroundStyle(.white)
                        .frame(width: 72, height: 72)
                        .background(
                            LinearGradient(colors: [.green, .green.opacity(0.8)],
                                           startPoint: .topLeading, endPoint: .bottomTrailing)
                        )
                        .clipShape(Circle())
                        .shadow(color: .green.opacity(0.4), radius: 12, x: 0, y: 6)
                }

                Text(linkStatus == .failed ? "重新拨打" : "拨打")
                    .font(.subheadline.bold())
                    .foregroundStyle(.primary)

                if !linkStatusMessage.isEmpty && linkStatus == .failed {
                    Text(linkStatusMessage)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                        .multilineTextAlignment(.center)
                        .padding(.horizontal, 40)
                }
            }

            Spacer()
                .frame(height: 80)
        }
    }

    // MARK: - 相机ON字幕：浮动框（默认2行高度，可滚动）

    private var cameraSubtitleOverlay: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 6) {
                    if messages.isEmpty {
                        Text("请说话")
                            .font(.subheadline)
                            .foregroundStyle(Color.primary.opacity(0.5))
                            .id("placeholder")
                    } else {
                        ForEach(messages) { msg in
                            Text(msg.content)
                                .font(.subheadline)
                                .foregroundStyle(msg.sender == .user ? Color.primary.opacity(0.5) : Color.primary)
                                .frame(maxWidth: .infinity, alignment: msg.sender == .user ? .trailing : .leading)
                                .id(msg.id)
                        }
                    }
                }
                .padding(.horizontal, 16)
                .padding(.vertical, 10)
            }
            .scrollIndicators(.hidden)
            .frame(maxWidth: .infinity)
            .frame(maxHeight: 64)
            .background(.ultraThinMaterial)
            .clipShape(RoundedRectangle(cornerRadius: 12))
            .padding(.horizontal, 24)
            .padding(.top, 60)
            .frame(maxHeight: .infinity, alignment: .top)
            .onAppear {
                if let last = messages.last {
                    proxy.scrollTo(last.id, anchor: .bottom)
                }
            }
            .onChange(of: messages.count) { _ in
                if let last = messages.last {
                    withAnimation(.easeOut(duration: 0.2)) {
                        proxy.scrollTo(last.id, anchor: .bottom)
                    }
                }
            }
            .onChange(of: messages.last?.content) { _ in
                if let last = messages.last {
                    withAnimation(.easeOut(duration: 0.2)) {
                        proxy.scrollTo(last.id, anchor: .bottom)
                    }
                }
            }
        }
    }

    private var chatMessages: some View {
        ScrollViewReader { proxy in
            ScrollView {
                LazyVStack(alignment: .leading, spacing: 10) {
                    ForEach(messages) { msg in
                        chatRow(msg).id(msg.id)
                    }
                }
                .padding(.bottom, 4)
            }
            .scrollIndicators(.hidden)
            .onAppear {
                scrollToBottom(proxy: proxy)
            }
            .onChange(of: messages.count) { _ in
                scrollToBottom(proxy: proxy)
            }
            .onChange(of: messages.last?.content) { _ in
                scrollToBottom(proxy: proxy)
            }
        }
    }

    private func scrollToBottom(proxy: ScrollViewProxy) {
        guard let last = messages.last else { return }
        withAnimation(.easeOut(duration: 0.2)) {
            proxy.scrollTo(last.id, anchor: .bottom)
        }
    }

    @ViewBuilder
    private func chatRow(_ msg: ChatMessage) -> some View {
        HStack(alignment: .top, spacing: 6) {
            if msg.sender == .user {
                Spacer(minLength: 30)
                Text(msg.content)
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            } else {
                Text(msg.content)
                    .font(.subheadline)
                    .foregroundStyle(.primary)
                Spacer(minLength: 30)
            }
        }
    }

    // MARK: - Input Bar（暂时隐藏，保留实现）

    private var inputBar: some View {
        HStack(spacing: 8) {
            if isVoiceMode {
                pushToTalkButton
            } else {
                textInputField
            }

            inputSideButton(systemName: isVoiceMode ? "keyboard" : "waveform") {
                isVoiceMode.toggle()
                if !isVoiceMode {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
                        isInputFocused = true
                    }
                } else {
                    isInputFocused = false
                }
            }
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 8)
        .background(.ultraThinMaterial)
        .clipShape(Capsule())
        .overlay(
            Capsule()
                .stroke(Color.white.opacity(0.15), lineWidth: 0.5)
        )
    }

    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(.subheadline.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.blue)
                    .clipShape(Circle())
            }
            .disabled(inputText.isEmpty)
        }
        .padding(.vertical, 4)
        .padding(.trailing, 4)
        .frame(maxWidth: .infinity)
        .background(Color(uiColor: .systemBackground).opacity(0.6))
        .clipShape(Capsule())
    }

    private func sendText() {
        let t = inputText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !t.isEmpty else { return }
        inputText = ""
        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: - Bottom Controls

    private var bottomControls: some View {
        VStack(spacing: 10) {
            HStack(spacing: 6) {
                Circle()
                    .fill(statusColor)
                    .frame(width: 8, height: 8)
                Text(statusText)
                    .font(.caption)
                    .foregroundStyle(cameraActive ? .white.opacity(0.85) : .secondary)
            }

            HStack(spacing: 0) {
                glassControlButton(
                    title: isMicOn ? "麦克风" : "静音",
                    systemName: isMicOn ? "mic.fill" : "mic.slash.fill",
                    isActive: isMicOn
                ) {
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    toggleMic()
                }

                glassControlButton(
                    title: cameraActive ? "摄像头" : "已关闭",
                    systemName: cameraActive ? "video.fill" : "video.slash.fill",
                    isActive: cameraActive
                ) {
                    UIImpactFeedbackGenerator(style: .light).impactOccurred()
                    if cameraActive { switchToBackgroundMode() } else { switchToCameraMode() }
                }

                glassControlButton(
                    title: "打断",
                    systemName: "stop.fill",
                    isActive: true
                ) {
                    UIImpactFeedbackGenerator(style: .medium).impactOccurred()
                    mgr.interruptAudioPlayer()
                }

                if linkStatus == .connected {
                    glassControlButton(
                        title: "挂断",
                        systemName: "phone.down.fill",
                        isActive: true,
                        tintColor: .red
                    ) {
                        UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
                        teardown()
                        dismiss()
                    }
                } else if linkStatus == .allocating || linkStatus == .connecting {
                    VStack(spacing: 6) {
                        ProgressView()
                            .controlSize(.small)
                            .frame(width: 52, height: 52)
                            .background(cameraActive ? Color.black.opacity(0.25) : Color(uiColor: .secondarySystemBackground))
                            .clipShape(Circle())
                        Text("接通中")
                            .font(.caption2)
                            .foregroundStyle(cameraActive ? .white.opacity(0.85) : .secondary)
                    }
                    .frame(maxWidth: .infinity)
                } else {
                    glassControlButton(
                        title: linkStatus == .failed ? "重新拨打" : "拨打",
                        systemName: "phone.fill",
                        isActive: true,
                        tintColor: .green
                    ) {
                        UIImpactFeedbackGenerator(style: .medium).impactOccurred()
                        Task { await performLink() }
                    }
                }
            }
            .padding(.horizontal, 24)

            Text("内容由 AI 生成")
                .font(.caption2)
                .foregroundStyle(.tertiary)
                .padding(.top, 2)
        }
        .padding(.top, 12)
        .padding(.bottom, 16)
    }

    private func glassControlButton(title: String, systemName: String,
                                    isActive: Bool, tintColor: Color = .primary,
                                    action: @escaping () -> Void) -> some View {
        let effectiveTint: Color = (tintColor == .primary && cameraActive) ? .white : tintColor
        return VStack(spacing: 6) {
            Button(action: action) {
                Image(systemName: systemName)
                    .font(.system(size: 20, weight: .semibold))
                    .foregroundStyle(isActive ? effectiveTint : (cameraActive ? .white.opacity(0.5) : .secondary))
                    .frame(width: 52, height: 52)
                    .background {
                        if cameraActive {
                            Color.black.opacity(0.25)
                        } else {
                            Color(uiColor: .secondarySystemBackground)
                        }
                    }
                    .clipShape(Circle())
                    .overlay(
                        Circle()
                            .stroke(.primary.opacity((cameraActive && isActive) ? 0.15 : 0.06), lineWidth: 0.5)
                    )
            }
            .buttonStyle(.plain)
            Text(title)
                .font(.caption2)
                .foregroundStyle(cameraActive ? .white.opacity(0.85) : .secondary)
        }
        .frame(maxWidth: .infinity)
    }

    // MARK: - Status helpers

    private var statusColor: Color {
        if linkStatus != .connected {
            switch linkStatus {
            case .idle:                    return mgr.isEngineCreated ? .yellow : .gray
            case .allocating, .connecting: return .orange
            case .connected:               return .green
            case .failed:                  return .red
            }
        }
        switch conversationState {
        case .idle:             return .green
        case .listening:        return .green
        case .thinking:         return .orange
        case .responding:       return .blue
        case .error:            return .red
        }
    }

    private var statusText: String {
        if linkStatus != .connected {
            switch linkStatus {
            case .idle:        return mgr.isConnected ? "通话中" : "未连接"
            case .allocating:  return "请求中"
            case .connecting:  return "接通中"
            case .connected:   return "通话中"
            case .failed:      return "失败"
            }
        }
        switch conversationState {
        case .idle:             return "通话中"
        case .listening:        return "聆听中"
        case .thinking:         return "思考中"
        case .responding:       return "回复中"
        case .error(let err):
            switch err {
            case .dataInputInspectionFailed: return "内容审核失败"
            case .unknown:                   return "发生错误"
            }
        }
    }

    // MARK: - Side Panel（迁移自视频理解的面包菜单导航）

    private var sidePanelOverlay: some View {
        ZStack(alignment: .leading) {
            Color.black.opacity(0.3)
                .ignoresSafeArea()
                .onTapGesture {
                    withAnimation(.easeInOut(duration: 0.25)) {
                        showSidePanel = false
                        selectedSetting = nil
                    }
                }
            sidePanelContent
                .frame(width: UIScreen.main.bounds.width * 0.78)
                .frame(maxHeight: .infinity)
                .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)
        .padding(.top, 44)
    }

    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) {
            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("音色").font(.caption).foregroundStyle(.secondary)
                HStack(spacing: 8) {
                    Menu {
                        ForEach(SessionConfig.presetVoices, id: \.self) { name in
                            Button(name) {
                                selectedVoice = name
                                SessionConfig.saveVoice(name)
                            }
                        }
                    } label: {
                        HStack(spacing: 4) {
                            Text(selectedVoice)
                                .font(.subheadline)
                            Image(systemName: "chevron.down")
                                .font(.caption)
                                .foregroundStyle(.blue)
                        }
                        .padding(.horizontal, 10)
                        .padding(.vertical, 6)
                        .background(Color(uiColor: .tertiarySystemFill))
                        .clipShape(RoundedRectangle(cornerRadius: 6))
                    }
                    Spacer()
                }
            }

            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()

            if linkStatus == .connected {
                Button {
                    Task {
                        mgr.stopAudioCapture()
                        mgr.stopAudioPlayer()
                        mgr.disconnect()
                        linkStatus = .idle
                        conversationState = .idle
                        messages.removeAll()
                        try? await Task.sleep(nanoseconds: 300_000_000)
                        await performLink()
                    }
                } label: {
                    Text("重新拨打")
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
            } else if linkStatus == .connecting || linkStatus == .allocating {
                HStack {
                    ProgressView().controlSize(.small)
                    Text("接通中...")
                        .font(.subheadline)
                        .foregroundStyle(.secondary)
                }
            } else {
                Button {
                    Task { await performLink() }
                } label: {
                    Text("拨打")
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .tint(.green)
            }

            if !linkStatusMessage.isEmpty {
                HStack(spacing: 6) {
                    Circle()
                        .fill(statusColor)
                        .frame(width: 8, height: 8)
                    Text(linkStatusMessage)
                        .font(.caption)
                        .foregroundStyle(statusColor)
                        .lineLimit(3)
                }
            }
        }
        .padding(12)
        .background(Color(uiColor: .secondarySystemGroupedBackground))
        .clipShape(RoundedRectangle(cornerRadius: 10))
    }

    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)
        }
    }

    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: 12) {
            Toggle("显示完整消息", isOn: $showRawMessage)
                .font(.subheadline)

            HStack {
                Text("接通耗时").font(.subheadline).bold()
                Spacer()
                if let ms = connectionLatencyMs {
                    Text("\(ms) ms")
                        .font(.subheadline.monospacedDigit())
                        .foregroundStyle(.secondary)
                } else {
                    Text("--")
                        .font(.subheadline)
                        .foregroundStyle(.tertiary)
                }
            }

            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: - Camera lifecycle

    private func setup() {
        if !mgr.isEngineCreated {
            mgr.createEngine(workDir: NSTemporaryDirectory(), enableDumpAudio: false, extras: "")
        }
        startVideoCapture()
        mgr.startAudioCapture(channel: 1, isExternal: false, isVoipMode: false)
        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()
        }
        mgr.clearLog()
    }

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

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

    private func switchToBackgroundMode() {
        if renderViewBound {
            mgr.setLocalView(nil, trackType: .video)
            renderViewBound = false
        }
        if mgr.isEngineCreated {
            mgr.stopVideoCapture()
        }
        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) {
            startVideoCapture()
        }
    }

    private func toggleMic() {
        isMicOn.toggle()
        guard mgr.isConnected else { return }
        if isMicOn {
            mgr.startAudioCapture(channel: 1, isExternal: false, isVoipMode: true)
        } else {
            mgr.stopAudioCapture()
        }
    }

    private func toggleSpeaker() {
        mgr.isSpeakerOn.toggle()
        if mgr.isSpeakerOn {
            mgr.startAudioPlayer(channel: 1, isExternal: false)
        } else {
            mgr.stopAudioPlayer()
        }
    }

    // MARK: - Parsed Message Handling

    private func handleParsedIncomingMessage(_ text: String) {
        guard let data = text.data(using: .utf8),
              let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let type = obj["type"] as? String else {
            return
        }
        switch type {
        case "conversation.item.input_audio_transcription.completed":
            if let transcript = obj["transcript"] as? String, !transcript.isEmpty {
                let asrMessage = ChatMessage(sender: .user, content: transcript, time: Date())
                // 插入到第一条还没有前置用户消息的 AI 消息之前，确保多轮对话正确配对
                var insertIndex = messages.count
                for i in 0..<messages.count {
                    if messages[i].sender == .ai {
                        if i == 0 || messages[i - 1].sender != .user {
                            insertIndex = i
                            break
                        }
                    }
                }
                messages.insert(asrMessage, at: insertIndex)
            }
        case "response.audio_transcript.delta":
            if let delta = obj["delta"] as? String, !delta.isEmpty {
                let itemId = obj["item_id"] as? String
                if let itemId = itemId,
                   let idx = messages.lastIndex(where: { $0.sender == .ai && $0.apiMessageId == itemId }) {
                    messages[idx].content += delta
                } else {
                    messages.append(ChatMessage(sender: .ai, content: delta, time: Date(), apiMessageId: itemId))
                }
            }
        default:
            break
        }
    }

    // MARK: - Link Flow

    private func performLink() async {
        // 每次开始通话时重置为后置相机
        isFrontCamera = false
        connectionLatencyMs = nil

        // 获取 token
        linkStatus = .allocating
        linkStatusMessage = "获取凭证..."
        do {
            let alloc = try await AppServerService.allocate(
                baseURL: AppServerConfig.currentBaseURL,
                model: model
            )
            token = alloc.aoqTokenForClient
            sid = alloc.sid
            certificate = alloc.clientRelayCertFingerprint
            relayEndpoints = alloc.clientRelayEndpoints.map { item in
                let ep = AoqRelayEndpoint()
                ep.endpoint = item.endpoint
                ep.port = item.port
                return ep
            }
            workspaceIdHash = alloc.extraInfo?.workspaceIdHash ?? ""
        } catch {
            linkStatus = .failed
            linkStatusMessage = "获取凭证失败: \(error.localizedDescription)"
            return
        }

        linkStatus = .allocating
        linkStatusMessage = "正在初始化..."

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

        // 应用音频编解码配置
        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)

        // 应用视频编码配置
        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)

        linkStatus = .connecting
        linkStatusMessage = "正在接通..."
        let audioTrack = AoqTrackParam()
        audioTrack.trackType = .audio
        let videoTrack = AoqTrackParam()
        videoTrack.trackType = .video
        let dataTrack = AoqTrackParam()
        dataTrack.trackType = .data
        connectionStartTime = Date()
        mgr.connect(
            token: token, sid: sid, certificate: certificate,
            relayEndpoints: relayEndpoints,
            workspaceIdHash: workspaceIdHash,
            publishTracks: [audioTrack, videoTrack, dataTrack],
            subscribeTracks: [audioTrack, dataTrack]
        )
    }

    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: - UIView Bridge for SDK Render Target

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

    func makeUIView(context: Context) -> UIView {
        let v = UIView()
        v.backgroundColor = .black
        v.isUserInteractionEnabled = false
        DispatchQueue.main.async { onReady(v) }
        return v
    }

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

// MARK: - Swipe Back Enabler

private struct PCSwipeBackEnabler: 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 {
        PhoneCallView()
    }
}
