import SwiftUI
import VoiceCatCore
import ReplayKit

struct VoiceControlsView: View {
    @Bindable var session: SessionState
    @StateObject private var router = IOSAudioRouter.shared

    var body: some View {
        HStack(spacing: 20) {
            // Join/Leave Voice button (mirrors macOS micToggleButton)
            if session.voiceState.inputMode == .pushToTalk {
                PTTButton(session: session)
            } else {
                Button {
                    if session.voiceState.micActive {
                        session.stopMicStream()
                    } else {
                        session.startMicStream()
                    }
                } label: {
                    Text(session.voiceState.micActive ? "Leave Voice" : "Join Voice")
                        .font(.body.weight(.semibold))
                        .frame(minWidth: 110)
                        .padding(.vertical, 8)
                        .padding(.horizontal, 12)
                        .background(session.voiceState.micActive ? Color.green.opacity(0.2) : Color.accentColor.opacity(0.15), in: RoundedRectangle(cornerRadius: 8))
                        .foregroundStyle(session.voiceState.micActive ? .green : .accentColor)
                }
                .disabled(session.currentChannelId == 0)
                .accessibilityLabel(session.voiceState.micActive ? "Leave Voice — stop sending microphone audio" : "Join Voice — start sending microphone audio")
            }

            // Level meter
            LevelMeterView(level: session.voiceState.level)
                .frame(width: 80, height: 8)
                .accessibilityHidden(true)

            Spacer()

            // Speaker output toggle — force the built-in speaker instead of the earpiece when
            // no headphones/BT are connected. Mirrors the persisted Settings ▸ Audio toggle.
            Button {
                router.setForceSpeaker(!router.forceSpeaker)
            } label: {
                Image(systemName: router.forceSpeaker ? "speaker.wave.2.fill" : "speaker.fill")
                    .font(.title3)
                    .foregroundStyle(router.forceSpeaker ? Color.accentColor : .primary)
            }
            .accessibilityLabel(router.forceSpeaker
                ? "Speaker on — turn off to use the earpiece"
                : "Speaker off — turn on for speakerphone")

            // Self mute (disabled when not in voice)
            Button {
                session.setMute(!session.voiceState.selfMuted, deafened: session.voiceState.selfDeafened)
            } label: {
                Image(systemName: session.voiceState.selfMuted ? "mic.slash" : "mic")
                    .font(.title3)
                    .foregroundStyle(session.voiceState.selfMuted ? .red : .primary)
            }
            .disabled(!session.voiceState.micActive)
            .accessibilityLabel(session.voiceState.selfMuted ? "Unmute microphone" : "Mute microphone")

            // Self deafen (disabled when not in voice)
            Button {
                session.setMute(session.voiceState.selfMuted, deafened: !session.voiceState.selfDeafened)
            } label: {
                Image(systemName: session.voiceState.selfDeafened ? "headphones.slash" : "headphones")
                    .font(.title3)
                    .foregroundStyle(session.voiceState.selfDeafened ? .red : .primary)
            }
            .disabled(!session.voiceState.micActive)
            .accessibilityLabel(session.voiceState.selfDeafened ? "Undeafen" : "Deafen")

            // Share screen audio (ReplayKit system broadcast picker). The picker launches the
            // broadcast upload extension; the host's BroadcastAudioPump then owns + feeds the
            // SCREEN_AUDIO stream. Tinted while sharing.
            BroadcastPickerButton(isSharing: session.voiceState.screenSharing)
                .frame(width: 32, height: 32)

            // Disconnect
            Button(role: .destructive) {
                session.stopMicStream()
                session.client.disconnect()
            } label: {
                Image(systemName: "phone.down.fill")
                    .font(.title3)
                    .foregroundStyle(.red)
            }
            .accessibilityLabel("Disconnect from server")
        }
        .padding(.horizontal, 16)
        .padding(.vertical, 10)
        .background(.bar)
    }
}

// MARK: - PTT Button (DragGesture instead of NSEvent on iOS)

private struct PTTButton: View {
    @Bindable var session: SessionState
    @GestureState private var isPressing = false

    var body: some View {
        Circle()
            .fill(isPressing ? Color.blue : Color(.systemGray4))
            .frame(width: 44, height: 44)
            .overlay {
                Image(systemName: "mic.fill")
                    .foregroundStyle(isPressing ? .white : .primary)
            }
            .gesture(
                DragGesture(minimumDistance: 0)
                    .updating($isPressing) { _, state, _ in state = true }
                    .onChanged { _ in
                        if !isPressing { return }
                        if !session.voiceState.micActive { session.startMicStream() }
                        session.setPushToTalk(true)
                        UIImpactFeedbackGenerator(style: .medium).impactOccurred()
                    }
                    .onEnded { _ in
                        session.setPushToTalk(false)
                        session.stopMicStream()
                    }
            )
            .accessibilityLabel("Push to talk, hold to transmit")
            .accessibilityAddTraits(.isButton)
    }
}

// MARK: - Broadcast picker

/// Wraps `RPSystemBroadcastPickerView` (which contains its own button) and points it at our
/// broadcast upload extension. Tapping it shows the system broadcast picker; the user starts the
/// broadcast and our extension launches.
private struct BroadcastPickerButton: UIViewRepresentable {
    let isSharing: Bool

    func makeUIView(context: Context) -> RPSystemBroadcastPickerView {
        let picker = RPSystemBroadcastPickerView(frame: CGRect(x: 0, y: 0, width: 32, height: 32))
        picker.preferredExtension = "cat.voice.VoiceCatiOS.broadcast"
        picker.showsMicrophoneButton = false
        return picker
    }

    func updateUIView(_ uiView: RPSystemBroadcastPickerView, context: Context) {
        uiView.tintColor = isSharing ? .systemGreen : .label
        // RPSystemBroadcastPickerView owns an inner UIButton that VoiceOver focuses; its default
        // label is the picker's image name ("module icon"). Label the inner button directly —
        // a SwiftUI .accessibilityLabel on the representable doesn't reach it.
        let label = isSharing ? "Stop sharing screen audio" : "Share screen audio"
        for case let button as UIButton in uiView.subviews {
            button.accessibilityLabel = label
        }
    }
}

// MARK: - Level Meter

private struct LevelMeterView: View {
    let level: Float

    var body: some View {
        GeometryReader { geo in
            ZStack(alignment: .leading) {
                RoundedRectangle(cornerRadius: 4)
                    .fill(Color(.systemGray5))
                RoundedRectangle(cornerRadius: 4)
                    .fill(levelColor)
                    .frame(width: geo.size.width * CGFloat(min(level * 10, 1.0)))
                    .animation(.linear(duration: 0.05), value: level)
            }
        }
    }

    private var levelColor: Color {
        if level > 0.15 { return .orange }
        if level > 0.05 { return .green }
        return .green.opacity(0.5)
    }
}
