import SwiftUI

import AVKit

import PDFKit

import SwiftData



struct StudyContainerView: View {

    @Environment(\.modelContext) private var modelContext

    @Query private var sessions: [StudySession]



    let sessionID: String?



    var body: some View {

        if let sessionID,

           let session = sessions.first(where: { $0.id == sessionID }) {

            StudyView(session: session)

        } else {

            ContentUnavailableView(

                "没有进行中的课程",

                systemImage: "play.rectangle",

                description: Text("从首页货架或最近学习打开课程。")

            )

            .navigationTitle("学习")

        }

    }

}



struct StudyView: View {

    @Environment(\.modelContext) private var modelContext

    @Environment(\.scenePhase) private var scenePhase

    @Environment(\.horizontalSizeClass) private var horizontalSizeClass

    @Environment(\.dismiss) private var dismiss

    @Bindable var session: StudySession



    @StateObject private var progressSaver = ProgressSaver()

    @StateObject private var chatController: StudyChatController



    @State private var player: AVPlayer?

    @State private var pdfDocument: PDFDocument?

    @State private var pdfPage: Int = 1

    @State private var splitRatio: Double = 0.55

    @State private var orientation: SplitOrientation = .horizontal

    @State private var layoutOrientation: SplitOrientation = .horizontal

    @State private var rightMode: StudyRightPanelMode = .hidden

    @State private var restoreDone = false

    @State private var dragStartRatio: Double?

    @State private var showCompactChat = false

    @State private var clipStart: Double?

    @State private var clipEnd: Double?

    @State private var endObserver: NSObjectProtocol?

    @State private var toastMessage: String?



    init(session: StudySession) {

        self.session = session

        _chatController = StateObject(wrappedValue: StudyChatController(

            sessionId: session.id,

            scenario: StudyView.scenario(for: session.mode)

        ))

    }



    private var courseStore: CourseStore { CourseStore(modelContext: modelContext) }

    private var libraryStore: LibraryStore { LibraryStore(modelContext: modelContext) }



    private var hasPdf: Bool {

        switch session.mode {

        case .singlePdf, .videoPdf, .pdfAudio, .dualPdf: return true

        default: return false

        }

    }



    private var isSplitMode: Bool {

        switch session.mode {

        case .videoPdf, .pdfAudio, .dualVideo, .dualPdf: return true

        default: return false

        }

    }



    private var isVideoMode: Bool {

        switch session.mode {

        case .singleVideo, .videoPdf, .dualVideo: return true

        default: return false

        }

    }



    private var isAudioMode: Bool {

        switch session.mode {

        case .singleAudio, .pdfAudio: return true

        default: return false

        }

    }



    private var useBottomChrome: Bool { isVideoMode }



    private var hasNextLesson: Bool { courseStore.hasNextItem(session: session) }



    private var maxPdfPage: Int { max(1, pdfDocument?.pageCount ?? 1) }



    private var useRegularLayout: Bool { horizontalSizeClass == .regular }



    /// PC-style: video study keeps PDF in the right panel, not split in the center stage.

    private var pdfInRightPanel: Bool {

        useRegularLayout && session.mode == .videoPdf && orientation == .horizontal

    }



    var body: some View {

        VStack(spacing: 0) {

            if hasNextLesson {

                albumAdvanceBar

            }

            mainStage

        }

        .frame(maxWidth: .infinity, maxHeight: .infinity)

        .background(isVideoMode && useRegularLayout ? Color.black : Color(.systemBackground))

        .navigationBarTitleDisplayMode(.inline)

        .navigationBarBackButtonHidden(true)

        .toolbar { studyToolbar }

        .sheet(isPresented: $showCompactChat) {

            NavigationStack {

                StudyAskChatView(

                    controller: chatController,

                    sessionTitle: session.title,

                    showStudyTools: isVideoMode,

                    onPrevSentence: isVideoMode ? { markPrevSentence() } : nil,

                    onCapture: { chatController.attachVideoFrame(from: player) },

                    onAudioAttach: isAudioMode ? { markClipEnd() } : nil,

                    onVideoAttach: { chatController.attachVideoFrame(from: player) }

                )

                .navigationTitle("AI 助手")

                .toolbar {

                    ToolbarItem(placement: .cancellationAction) {

                        Button("关闭") { showCompactChat = false }

                    }

                }

            }

        }

        .onAppear(perform: setup)

        .onDisappear {

            chatController.flush()

            chatController.stopSpeaking()

            persist(force: true)

            removeEndObserver()

        }

        .onChange(of: scenePhase) { _, phase in

            if phase != .active {

                chatController.flush()

                persist(force: true)

            }

        }

        .onChange(of: pdfPage) { _, _ in persist(force: true) }

        .onReceive(Timer.publish(every: 60, on: .main, in: .common).autoconnect()) { _ in

            persist(force: true)

        }

        .overlay(alignment: .top) {

            if let toastMessage {

                Text(toastMessage)

                    .font(.caption)

                    .padding(.horizontal, 12)

                    .padding(.vertical, 8)

                    .background(.ultraThinMaterial, in: Capsule())

                    .padding(.top, 8)

                    .onAppear {

                        DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {

                            self.toastMessage = nil

                        }

                    }

            }

        }

    }



    @ToolbarContentBuilder

    private var studyToolbar: some ToolbarContent {

        ToolbarItem(placement: .topBarLeading) {

            Button {

                chatController.flush()

                persist(force: true)

                dismiss()

            } label: {

                Image(systemName: "chevron.backward")

            }

            .accessibilityLabel("返回")

        }

        ToolbarItemGroup(placement: .topBarTrailing) {

            if isVideoMode && !useRegularLayout {

                Button { openChat() } label: {

                    Image(systemName: "message")

                }

                .accessibilityLabel("AI 对话")

            }



            if isSplitMode && !pdfInRightPanel {

                Button { toggleOrientation() } label: {

                    Image(systemName: orientation == .horizontal ? "rectangle.portrait.rotate" : "rectangle.landscape.rotate")

                }

                .accessibilityLabel("切换方向")

            }



            if hasPdf && (!pdfInRightPanel || rightMode == .pdf) {

                Button { goPdfPage(-1) } label: {

                    Image(systemName: "chevron.left")

                }

                .disabled(pdfPage <= 1)

                Button { goPdfPage(1) } label: {

                    Image(systemName: "chevron.right")

                }

                .disabled(pdfPage >= maxPdfPage)

            }



            if useRegularLayout && (isVideoMode || hasPdf) {

                Button { cycleRightPanel() } label: {

                    Image(systemName: rightPanelIcon)

                }

                .accessibilityLabel("切换右侧面板")

            }

        }

    }



    private var rightPanelIcon: String {

        switch rightMode {

        case .hidden: return "sidebar.right"

        case .chat: return "sidebar.right"

        case .pdf: return "doc.richtext"

        }

    }



    private var albumAdvanceBar: some View {

        HStack {

            Text("还有下一课")

                .font(.subheadline)

                .foregroundStyle(.secondary)

            Spacer()

            Button("下一课") {

                advanceLesson()

            }

            .buttonStyle(.borderedProminent)

        }

        .padding(.horizontal, 16)

        .padding(.vertical, 8)

        .background(Color(.secondarySystemBackground))

    }



    @ViewBuilder

    private var mainStage: some View {

        if useRegularLayout && (isVideoMode || (!useBottomChrome && hasPdf)) {

            HStack(spacing: 0) {

                stageContent

                    .frame(maxWidth: .infinity, maxHeight: .infinity)

                if rightMode != .hidden {
                    Divider()
                    trailingPanel
                        .frame(minWidth: 320, idealWidth: 380, maxWidth: 480)
                        .background(Color.white)
                }

            }

        } else {

            stageContent

        }

    }



    @ViewBuilder

    private var trailingPanel: some View {

        switch rightMode {

        case .chat:

            StudyAskChatView(

                controller: chatController,

                sessionTitle: session.title,

                showStudyTools: isVideoMode,

                onCollapse: { collapseChatPanel() },

                onPrevSentence: isVideoMode ? { markPrevSentence() } : nil,

                onCapture: {

                    if isVideoMode {

                        chatController.attachVideoFrame(from: player)

                    } else {

                        chatController.attachPdfPage(from: pdfDocument, page: pdfPage)

                    }

                },

                onAudioAttach: isAudioMode || isVideoMode ? { markClipEnd() } : nil,

                onVideoAttach: isVideoMode ? { chatController.attachVideoFrame(from: player) } : nil

            )

        case .pdf:

            if let pdfDocument {

                StudyPdfPanelView(document: pdfDocument, pageIndex: $pdfPage)

            } else {

                ContentUnavailableView("无 PDF", systemImage: "doc")

            }

        case .hidden:

            EmptyView()

        }

    }



    @ViewBuilder

    private var stageContent: some View {

        GeometryReader { geo in

            let autoLayout: SplitOrientation = geo.size.width >= geo.size.height ? .horizontal : .vertical

            Group {

                switch session.mode {

                case .singleVideo, .singleAudio:

                    mediaPane

                case .singlePdf:

                    pdfPane

                case .videoPdf:

                    if pdfInRightPanel {

                        mediaPane

                    } else {

                        split(orientation: effectiveOrientation(auto: autoLayout), geo: geo) {

                            mediaPane

                        } secondary: {

                            pdfPane

                        }

                    }

                case .pdfAudio:

                    split(orientation: effectiveOrientation(auto: autoLayout), geo: geo) {

                        pdfPane

                    } secondary: {

                        mediaPane

                    }

                case .dualVideo, .dualPdf:

                    ContentUnavailableView(

                        "模式暂未产品化",

                        systemImage: "rectangle.split.2x1",

                        description: Text("dual_* 已可登记，MVP Study UI 未启用。")

                    )

                }

            }

            .onAppear { layoutOrientation = autoLayout }

            .onChange(of: geo.size) { _, size in

                layoutOrientation = size.width >= size.height ? .horizontal : .vertical

            }

        }

    }



    private func effectiveOrientation(auto: SplitOrientation) -> SplitOrientation {

        if session.mode == .videoPdf || session.mode == .pdfAudio {

            return orientation

        }

        return auto

    }



    private var mediaPane: some View {

        Group {

            if let player {

                if session.mode == .singleAudio || session.mode == .pdfAudio {

                    VStack {

                        Image(systemName: "waveform")

                            .font(.system(size: 48))

                            .foregroundStyle(.secondary)

                        VideoPlayer(player: player)

                            .frame(height: 80)

                    }

                    .frame(maxWidth: .infinity, maxHeight: .infinity)

                    .background(Color.black.opacity(0.04))

                } else {

                    ZStack(alignment: .bottom) {

                        Color.black

                        VideoPlayer(player: player)

                        if useBottomChrome {

                            StudyBottomChromeView(

                                chat: chatController,

                                player: player,

                                onCapture: { chatController.attachVideoFrame(from: player) }

                            )

                        }

                    }

                }

            } else {

                ContentUnavailableView("无法加载媒体", systemImage: "exclamationmark.triangle")

            }

        }

    }



    private var pdfPane: some View {

        Group {

            if let pdfDocument {

                PDFKitView(document: pdfDocument, pageIndex: $pdfPage)

            } else {

                ContentUnavailableView("无法加载 PDF", systemImage: "doc.richtext")

            }

        }

        .simultaneousGesture(

            DragGesture(minimumDistance: 24)

                .onChanged { _ in persist(force: false) }

        )

    }



    @ViewBuilder

    private func split<Primary: View, Secondary: View>(

        orientation: SplitOrientation,

        geo: GeometryProxy,

        @ViewBuilder primary: () -> Primary,

        @ViewBuilder secondary: () -> Secondary

    ) -> some View {

        let primaryView = primary()

        let secondaryView = secondary()

        if orientation == .horizontal {

            let total = geo.size.width

            let gap: CGFloat = 10

            let first = total * splitRatio

            let second = total - first - gap

            HStack(spacing: 0) {

                primaryView.frame(width: max(120, first))

                divider(axis: .horizontal, total: total)

                secondaryView.frame(width: max(120, second))

            }

        } else {

            let total = geo.size.height

            let gap: CGFloat = 10

            let first = total * splitRatio

            let second = total - first - gap

            VStack(spacing: 0) {

                primaryView.frame(height: max(120, first))

                divider(axis: .vertical, total: total)

                secondaryView.frame(height: max(120, second))

            }

        }

    }



    private func divider(axis: Axis, total: CGFloat) -> some View {

        let barThickness: CGFloat = 10

        return ZStack {

            Color.clear

            RoundedRectangle(cornerRadius: 2)

                .fill(Color.secondary.opacity(0.45))

                .frame(

                    width: axis == .horizontal ? 4 : 48,

                    height: axis == .vertical ? 4 : 48

                )

        }

        .frame(

            width: axis == .horizontal ? barThickness : nil,

            height: axis == .vertical ? barThickness : nil

        )

        .frame(maxWidth: axis == .vertical ? .infinity : nil,

               maxHeight: axis == .horizontal ? .infinity : nil)

        .contentShape(Rectangle())

        .gesture(

            DragGesture(minimumDistance: 0)

                .onChanged { value in

                    if dragStartRatio == nil { dragStartRatio = splitRatio }

                    let delta = axis == .horizontal ? value.translation.width : value.translation.height

                    let next = (dragStartRatio ?? splitRatio) + Double(delta / max(total, 1))

                    splitRatio = min(0.8, max(0.2, next))

                }

                .onEnded { _ in

                    dragStartRatio = nil

                    persist(force: true)

                }

        )

        .accessibilityLabel("拖拽调整分屏")

    }



    private func setup() {

        guard !restoreDone else { return }

        restoreDone = true

        pdfPage = max(1, session.pdfPage)

        splitRatio = session.splitRatio

        orientation = session.orientation

        layoutOrientation = session.orientation



        chatController.loadHistory()



        let videoURL = resolveURL(session.videoId)

        let audioURL = resolveURL(session.audioId)

        let mediaURL = videoURL ?? audioURL

        if let mediaURL {

            let item = AVPlayerItem(url: mediaURL)

            let av = AVPlayer(playerItem: item)

            player = av

            let time = CMTime(seconds: session.mediaTime, preferredTimescale: 600)

            av.seek(to: time)

            installEndObserver(for: item)

        }



        if let pdfURL = resolveURL(session.pdfId) {

            pdfDocument = PDFDocument(url: pdfURL)

        }



        if isVideoMode && useRegularLayout {

            rightMode = .chat

        }

    }



    private func installEndObserver(for item: AVPlayerItem) {

        removeEndObserver()

        endObserver = NotificationCenter.default.addObserver(

            forName: .AVPlayerItemDidPlayToEndTime,

            object: item,

            queue: .main

        ) { _ in

            handleMediaEnded()

        }

    }



    private func removeEndObserver() {

        if let endObserver {

            NotificationCenter.default.removeObserver(endObserver)

            self.endObserver = nil

        }

    }



    private func handleMediaEnded() {

        guard hasNextLesson else { return }

        switch session.mode {

        case .singleVideo, .singleAudio:

            advanceLesson(auto: true)

        default:

            break

        }

    }



    private func advanceLesson(auto: Bool = false) {

        do {

            try courseStore.advanceToNextItem(session: session)

            reloadSessionMedia()

            if auto { toastMessage = "已自动进入下一课" }

        } catch {

            toastMessage = error.localizedDescription

        }

    }



    private func reloadSessionMedia() {

        removeEndObserver()

        player?.pause()

        player = nil

        pdfDocument = nil

        pdfPage = max(1, session.pdfPage)

        splitRatio = session.splitRatio

        orientation = session.orientation



        let videoURL = resolveURL(session.videoId)

        let audioURL = resolveURL(session.audioId)

        if let mediaURL = videoURL ?? audioURL {

            let item = AVPlayerItem(url: mediaURL)

            let av = AVPlayer(playerItem: item)

            player = av

            installEndObserver(for: item)

        }

        if let pdfURL = resolveURL(session.pdfId) {

            pdfDocument = PDFDocument(url: pdfURL)

        }

    }



    private func openChat() {

        if useRegularLayout {

            rightMode = .chat

        } else {

            showCompactChat = true

        }

    }



    private func toggleChatPanel() {

        rightMode = rightMode == .chat ? .hidden : .chat

    }



    private func collapseChatPanel() {

        rightMode = .hidden

    }



    private func cycleRightPanel() {

        if isVideoMode {

            switch rightMode {

            case .hidden:

                rightMode = pdfInRightPanel && pdfDocument != nil ? .pdf : .chat

            case .pdf:

                rightMode = .chat

            case .chat:

                rightMode = .hidden

            }

            return

        }

        switch rightMode {

        case .hidden:

            rightMode = hasPdf ? .pdf : .chat

        case .pdf:

            rightMode = .chat

        case .chat:

            rightMode = .hidden

        }

    }



    private func toggleOrientation() {

        orientation = orientation == .horizontal ? .vertical : .horizontal

        if pdfInRightPanel && rightMode == .pdf {

            rightMode = .chat

        }

        persist(force: true)

    }



    private func markPrevSentence() {

        player?.pause()

        let t = player?.currentTime().seconds ?? 0

        let start = max(0, t - 10)

        chatController.setAudioClip(start: start, end: t)

        chatController.attachVideoFrame(from: player)

        if chatController.question.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {

            chatController.question = "这段话是什么意思？里面男声、女声分别说了什么？和前文有什么关系？"

        }

        toastMessage = String(format: "已标记上一句 %.1f–%.1fs", start, t)

    }



    private func markClipEnd() {

        let t = player?.currentTime().seconds ?? 0

        if clipStart == nil {

            clipStart = t

            toastMessage = String(format: "已标记开始 %.1fs", t)

        } else {

            clipEnd = t

            chatController.setAudioClip(start: clipStart ?? 0, end: t)

            toastMessage = String(format: "已附加片段 %.1f–%.1fs", clipStart ?? 0, t)

            clipStart = nil

            clipEnd = nil

        }

    }



    private func goPdfPage(_ delta: Int) {

        let next = min(maxPdfPage, max(1, pdfPage + delta))

        guard next != pdfPage else { return }

        pdfPage = next

        persist(force: true)

    }



    private func resolveURL(_ resourceId: String?) -> URL? {

        guard let resourceId else { return nil }

        if let courseId = session.courseId {

            return try? CourseLibrary.resolveMediaURL(courseId: courseId, resourceId: resourceId)

        }

        return libraryStore.fileURL(forResourceId: resourceId)

    }



    private func persist(force: Bool) {

        let mediaTime = player?.currentTime().seconds ?? session.mediaTime

        let safeTime = mediaTime.isFinite ? mediaTime : 0

        progressSaver.save(

            session: session,

            mediaTime: safeTime,

            pdfPage: pdfPage,

            splitRatio: splitRatio,

            orientation: orientation,

            force: force,

            store: libraryStore

        )

    }



    private static func scenario(for mode: StudyMode) -> StudyAskScenario {

        switch mode {

        case .singleVideo, .videoPdf, .dualVideo: return .video

        case .singleAudio, .pdfAudio: return .audio

        case .singlePdf, .dualPdf: return .pdf

        }

    }

}


