import AVFoundation
import SwiftUI

struct TestView: View {
    @ObservedObject var controller: PlayerController
    let onFullscreen: () -> Void

    @State private var recordingSession: AudioRecorderSession?
    @State private var recordingURL: URL?
    @State private var isRecording = false
    @State private var isAssessing = false
    @State private var testStatus = "请先在训练模块选择一个云端课程"
    @State private var scoreSummary = "尚未评测"
    @State private var scoreDetail = ""
    @State private var dubSegments: [Int: URL] = [:]
    @State private var dubRawSegments: [Int: URL] = [:]
    @State private var latestScores: [Int: ImitationAssessmentResult] = [:]
    @State private var mergedDubURL: URL?
    @State private var dubPlayer: AVPlayer?
    @State private var dubbingPlayback = false
    @State private var dubbingStatus = "配音：尚未生成"
    @State private var isGeneratingDub = false
    @State private var isSharingDub = false
    @State private var shareURL: URL?
    @State private var showShareSheet = false
    @State private var dubPlaybackObserver: NSObjectProtocol?
    @State private var testSubtitleVisible = true

    var body: some View {
        ScrollView(showsIndicators: false) {
            VStack(spacing: 0) {
                PlayerSectionView(
                    controller: controller,
                    showsContinuousToggle: false,
                    continuousPlayback: .constant(true),
                    showsSubtitle: $testSubtitleVisible,
                    onFullscreen: onFullscreen
                )
                if testSubtitleVisible {
                    Text(controller.currentSentence?.text ?? "当前句子")
                        .font(.system(size: 15))
                        .foregroundColor(Theme.textDark)
                        .frame(maxWidth: .infinity, alignment: .leading)
                }
                VStack(alignment: .leading, spacing: 10) {
                Text("朗读评测（测试）")
                    .font(.system(size: 16, weight: .bold))
                    .foregroundColor(Theme.textDark)
                Text(testStatus)
                    .font(.system(size: 13))
                    .foregroundColor(Theme.accentDeep)
                    .fixedSize(horizontal: false, vertical: true)
                HStack(spacing: 8) {
                    Toggle("显示字幕", isOn: $testSubtitleVisible)
                        .font(.system(size: 13, weight: .bold))
                        .foregroundColor(Theme.textDark)
                        .tint(Theme.accent)
                }
                HStack(spacing: 8) {
                    sentenceButton("上一句") {
                        controller.seekToPreviousSentence()
                    }
                    sentenceButton("播放原句") {
                        controller.playCurrentSentenceAndStop()
                        controller.showTransientMessage("正在播放当前句子，播放完自动停止")
                    }
                    sentenceButton("下一句") {
                        controller.seekToNextSentence()
                    }
                }
                Button(action: toggleRecording) {
                    Text(isRecording ? "停止并评测" : "开始录音")
                        .font(.system(size: 14, weight: .bold))
                        .foregroundColor(.white)
                        .frame(maxWidth: .infinity)
                        .frame(height: 44)
                        .background(isRecording ? Theme.buttonBlue : Theme.accent)
                        .clipShape(RoundedRectangle(cornerRadius: 8))
                }
                .buttonStyle(.plain)
                .disabled(isAssessing)
                HStack(spacing: 8) {
                    dubButton("播放配音", enabled: !dubSegments.isEmpty && !dubbingPlayback && !isGeneratingDub) {
                        toggleDubPlayback()
                    }
                    dubButton("生成配音", enabled: !dubRawSegments.isEmpty && !dubbingPlayback && !isGeneratingDub) {
                        generateDubShareAudio()
                    }
                    dubButton("分享配音", enabled: mergedDubURL != nil && !dubbingPlayback && !isSharingDub) {
                        shareMergedDub()
                    }
                }
                Text(dubbingStatus)
                    .font(.system(size: 13))
                    .foregroundColor(Theme.accentDeep)
                    .frame(maxWidth: .infinity, alignment: .leading)
                Text(scoreSummary)
                    .font(.system(size: 22, weight: .bold))
                    .foregroundColor(Theme.textDark)
                Text(scoreDetail)
                    .font(.system(size: 13))
                    .foregroundColor(Theme.textSubtle)
                    .lineSpacing(4)
                    .frame(maxWidth: .infinity, alignment: .leading)
            }
            .padding(18)
            .background(Color.white)
            .clipShape(RoundedRectangle(cornerRadius: 8))
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(Theme.lightBorder, lineWidth: 1)
            )
                .padding(16)
            }
        }
        .onReceive(controller.$currentItem) { _ in
            refreshTestUI()
        }
        .onReceive(controller.$currentSentence) { _ in
            refreshTestUI()
        }
        .onDisappear {
            stopDubPlayback(message: nil)
        }
        .sheet(isPresented: $showShareSheet) {
            if let url = shareURL {
                ActivityShareSheet(items: [url])
            }
        }
    }

    private func sentenceButton(_ label: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            Text(label)
                .font(.system(size: 13, weight: .bold))
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .frame(height: 40)
                .background(Theme.buttonBlue)
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .buttonStyle(.plain)
    }

    private func dubButton(_ label: String, enabled: Bool, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            Text(label)
                .font(.system(size: 13, weight: .bold))
                .foregroundColor(.white)
                .frame(maxWidth: .infinity)
                .frame(height: 38)
                .background(enabled ? Theme.buttonBlue : Theme.border)
                .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .buttonStyle(.plain)
        .disabled(!enabled)
    }

    // MARK: - 录音

    private func toggleRecording() {
        if isRecording {
            stopRecordingAndAssess()
        } else {
            startRecording()
        }
    }

    private func startRecording() {
        controller.pause()
        requestMicrophonePermission { granted in
            guard granted else {
                testStatus = "需要麦克风权限才能进行录音评测"
                return
            }
            beginRecording()
        }
    }

    private func requestMicrophonePermission(completion: @escaping (Bool) -> Void) {
        if #available(iOS 17.0, *) {
            AVAudioApplication.requestRecordPermission { granted in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        } else {
            AVAudioSession.sharedInstance().requestRecordPermission { granted in
                DispatchQueue.main.async {
                    completion(granted)
                }
            }
        }
    }

    private func beginRecording() {
        let fileURL = FileManager.default.temporaryDirectory
            .appendingPathComponent("attempt-\(Int(Date().timeIntervalSince1970 * 1000)).m4a")
        let newSession = AudioRecorderSession(outputURL: fileURL)
        do {
            let session = AVAudioSession.sharedInstance()
            try session.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
            try session.setActive(true)
            guard session.isInputAvailable else {
                testStatus = microphoneInputUnavailableMessage()
                return
            }
            do {
                try newSession.start()
            } catch {
                try session.setActive(false, options: .notifyOthersOnDeactivation)
                try session.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker])
                try session.setActive(true)
                try newSession.start()
            }
            recordingSession = newSession
            recordingURL = fileURL
            isRecording = true
            testStatus = "正在录音…读完当前句子后点击“停止并评测”"
        } catch AudioRecorderSessionError.inputUnavailable {
            newSession.cancel()
            testStatus = microphoneInputUnavailableMessage()
        } catch AudioRecorderSessionError.startFailed {
            newSession.cancel()
            testStatus = recordingStartFailedMessage()
        } catch {
            newSession.cancel()
            testStatus = "录音启动失败：\(error.localizedDescription)"
        }
    }

    private func microphoneInputUnavailableMessage() -> String {
        #if targetEnvironment(simulator)
        return "录音启动失败：未检测到麦克风输入。请在模拟器菜单 I/O → Audio Input 中选择输入设备，并允许“模拟器”使用 Mac 麦克风后重试"
        #else
        return "录音启动失败：未检测到麦克风输入。请检查 设置 → 隐私与安全性 → 麦克风 是否允许本 App 使用麦克风"
        #endif
    }

    private func recordingStartFailedMessage() -> String {
        #if targetEnvironment(simulator)
        return "录音启动失败：无法开始录制。模拟器请确认 I/O → Audio Input 已选择输入设备，再重试"
        #else
        return "录音启动失败：无法开始录制。请确认麦克风未被其他应用占用后重试"
        #endif
    }

    private func stopRecordingAndAssess() {
        guard let currentSession = recordingSession else {
            return
        }
        let duration: TimeInterval
        do {
            try currentSession.stop()
            duration = currentSession.recordDuration
        } catch {
            testStatus = "录音结束失败：\(error.localizedDescription)"
            currentSession.cancel()
            recordingSession = nil
            isRecording = false
            restorePlaybackAudioSession()
            return
        }
        restorePlaybackAudioSession()
        recordingSession = nil
        isRecording = false
        guard let url = recordingURL else {
            return
        }
        recordingURL = nil
        let rawURL = currentSession.rawRecordingURL
        if duration < 1.0 {
            testStatus = "录音太短或无法保存"
            try? FileManager.default.removeItem(at: url)
            try? FileManager.default.removeItem(at: rawURL)
            return
        }
        guard let sentence = controller.currentSentence else {
            testStatus = "没有可归属的句子，本次录音已丢弃"
            try? FileManager.default.removeItem(at: url)
            try? FileManager.default.removeItem(at: rawURL)
            return
        }
        if let oldURL = dubSegments[sentence.index] {
            try? FileManager.default.removeItem(at: oldURL)
        }
        if let oldRawURL = dubRawSegments[sentence.index] {
            try? FileManager.default.removeItem(at: oldRawURL)
        }
        dubSegments[sentence.index] = url
        dubRawSegments[sentence.index] = rawURL
        mergedDubURL = nil
        submitAssessment(recordingURL: url)
    }

    private func restorePlaybackAudioSession() {
        let session = AVAudioSession.sharedInstance()
        try? session.setCategory(.playback, mode: .moviePlayback)
        try? session.setActive(true)
    }

    // MARK: - 评测

    private func submitAssessment(recordingURL url: URL) {
        guard let item = controller.currentItem, let sentence = controller.currentSentence else {
            testStatus = "没有正在学习的句子，无法评测"
            return
        }
        guard item.id.range(of: AppConfig.sha256Pattern, options: .regularExpression) != nil else {
            testStatus = "仅云端课程支持朗读评测（本地视频请先上传到服务器）"
            return
        }
        guard !AppConfig.assessmentAPIKey.isEmpty,
              !AppConfig.assessmentAPIKey.hasPrefix("replace-") else {
            testStatus = "未配置评分密钥：请在 Config.swift 填入服务器 .env 的 CLIENT_API_KEY"
            return
        }
        testStatus = "正在评测第 \(sentence.index + 1) 句，请稍候…"
        scoreSummary = "评测中…"
        scoreDetail = ""
        isAssessing = true
        let request = ImitationAssessmentRequest(
            mediaId: item.id,
            sentence: sentence,
            recordingURL: url,
            locale: sentence.language?.isEmpty == false ? sentence.language : nil
        )
        Task {
            do {
                let result = try await ImitationAssessor.assess(request: request)
                applyAssessmentResult(result)
            } catch {
                testStatus = "评测失败：\(error.localizedDescription)"
                scoreSummary = "评测失败"
            }
            isAssessing = false
        }
    }

    private func applyAssessmentResult(_ result: ImitationAssessmentResult) {
        testStatus = result.passed == true ? "已通过" : "未通过"
        scoreSummary = String(format: "总分 %.1f", result.overallScore)
        var lines: [String] = []
        if let value = result.contentScore {
            lines.append(String(format: "内容分 %.1f", value))
        }
        if let value = result.fluencyScore {
            lines.append(String(format: "流畅度 %.1f", value))
        }
        if let value = result.durationScore {
            lines.append(String(format: "时长分 %.1f", value))
        }
        if let value = result.pauseScore {
            lines.append(String(format: "停顿分 %.1f", value))
        }
        if let value = result.speechRateScore {
            lines.append(String(format: "语速分 %.1f", value))
        }
        if let text = result.referenceText, !text.isEmpty {
            lines.append("参考：\(text)")
        }
        if let text = result.recognizedText, !text.isEmpty {
            lines.append("识别：\(text)")
        }
        if !result.missingTokens.isEmpty {
            lines.append("漏读：\(result.missingTokens.joined(separator: "、"))")
        }
        if !result.extraTokens.isEmpty {
            lines.append("多读：\(result.extraTokens.joined(separator: "、"))")
        }
        if let feedback = result.feedback, !feedback.isEmpty {
            lines.append(feedback)
        }
        scoreDetail = lines.joined(separator: "\n")
    }

    private func refreshTestUI() {
        guard !isRecording, !isAssessing else {
            return
        }
        guard let item = controller.currentItem, let sentence = controller.currentSentence else {
            testStatus = "请先在训练模块选择一个云端课程"
            return
        }
        let currentRecording = dubSegments[sentence.index]
        let segmentLabel = currentRecording == nil ? "" : " · 本句已录音，重录将覆盖"
        testStatus = "评测对象：第 \(sentence.index + 1) 句（共 \(item.sentences.count) 句）\(segmentLabel)"
        if mergedDubURL == nil {
            dubbingStatus = dubSegments.isEmpty ? "配音：请先录音" : "配音：已保存 \(dubSegments.count) 句，可生成配音"
        }
    }

    // MARK: - 配音

    private func toggleDubPlayback() {
        if dubbingPlayback {
            stopDubPlayback(message: "已停止配音播放")
            return
        }
        let sentence = controller.currentSentence
        let audioURL = mergedDubURL ?? sentence.flatMap { dubSegments[$0.index] }
        guard let audioURL else { return }
        if mergedDubURL == nil, let sentence = sentence {
            controller.seek(to: sentence.startMs)
        }
        controller.pause()
        controller.setVolume(0)
        let player = dubPlayer ?? AVPlayer()
        dubPlayer = player
        player.replaceCurrentItem(with: AVPlayerItem(url: audioURL))
        dubbingPlayback = true
        dubbingStatus = mergedDubURL == nil ? "正在播放当前句配音，视频原声静音" : "正在播放整段配音，视频原声静音"
        player.play()
        if dubPlaybackObserver == nil {
            dubPlaybackObserver = NotificationCenter.default.addObserver(
                forName: AVPlayerItem.didPlayToEndTimeNotification,
                object: player.currentItem,
                queue: .main
            ) { _ in
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
                    stopDubPlayback(message: "配音播放完成")
                }
            }
        }
    }

    private func stopDubPlayback(message: String?) {
        dubPlayer?.pause()
        dubPlayer?.seek(to: .zero)
        controller.pause()
        controller.setVolume(1)
        dubbingPlayback = false
        if let dubPlaybackObserver {
            NotificationCenter.default.removeObserver(dubPlaybackObserver)
            self.dubPlaybackObserver = nil
        }
        if let message {
            dubbingStatus = message
        }
    }

    private func generateDubShareAudio() {
        guard !dubRawSegments.isEmpty else { return }
        let segments = dubRawSegments.sorted { $0.key < $1.key }
        let outputURL = FileManager.default.temporaryDirectory
            .appendingPathComponent("dubbing-\(Int(Date().timeIntervalSince1970 * 1000)).m4a")
        isGeneratingDub = true
        dubbingStatus = "正在合成 \(segments.count) 句配音…"
        Task {
            do {
                try await DubbingCompositor.merge(urls: segments.map(\.value), outputURL: outputURL)
                if let oldURL = mergedDubURL {
                    try? FileManager.default.removeItem(at: oldURL)
                }
                mergedDubURL = outputURL
                dubbingStatus = "已合成 \(segments.count) 句配音，可点击“分享配音”"
            } catch {
                dubbingStatus = "合成失败：\(error.localizedDescription)"
            }
            isGeneratingDub = false
        }
    }

    private func shareMergedDub() {
        guard let item = controller.currentItem, mergedDubURL != nil else { return }
        guard item.id.range(of: AppConfig.sha256Pattern, options: .regularExpression) != nil else {
            dubbingStatus = "分享失败：网页分享仅支持云端课程视频"
            return
        }
        isSharingDub = true
        dubbingStatus = "正在生成分享链接…"
        let request = DubShareUploadRequest(
            videoHash: item.id,
            title: item.title,
            segments: dubSegments.map { ($0.key, $0.value) },
            scores: latestScores
        )
        Task {
            do {
                let link = try await ImitationAssessor.uploadDubShare(request: request)
                shareURL = URL(string: link)
                showShareSheet = true
                dubbingStatus = "已生成分享链接：\(link)"
            } catch {
                dubbingStatus = "分享失败：\(error.localizedDescription)"
            }
            isSharingDub = false
        }
    }
}

struct ActivityShareSheet: UIViewControllerRepresentable {
    let items: [Any]

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

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}
