import SwiftUI

@MainActor
final class CatalogViewModel: ObservableObject {
    @Published private(set) var videos: [TrainingVideoSummary] = []
    @Published private(set) var statusText = "正在同步"
    @Published private(set) var activeVideoHash: String?

    weak var controller: PlayerController?

    private var baseURL = AppConfig.serverBaseURL
    private var hasLoaded = false

    func loadIfNeeded() async {
        guard !hasLoaded else {
            return
        }
        hasLoaded = true
        await load()
    }

    func load() async {
        statusText = "正在同步"
        do {
            let (fetched, base) = try await VideoCatalogAPI.fetch()
            baseURL = base
            videos = fetched
            statusText = fetched.isEmpty ? "暂无云端课程" : "\(fetched.count) 个云端课程"
        } catch {
            statusText = "云端暂不可用：\(error.localizedDescription)"
        }
    }

    /// 与安卓端 loadRemoteVideo() 一致：先以无句边界装载，再拉取句边界后重新装载。
    func select(_ video: TrainingVideoSummary) {
        guard let controller = controller else {
            return
        }
        activeVideoHash = video.videoHash
        guard let url = video.resolvedStreamURL(baseURL: baseURL) else {
            controller.boundaryState = .failed(message: "无法解析视频流地址")
            return
        }
        let loadingItem = TrainingMediaItem(id: video.videoHash, title: video.title, url: url)
        controller.load(loadingItem)
        controller.boundaryState = .loading(isLocal: false)
        controller.showTransientMessage("正在加载云端课程...")
        Task {
            do {
                let document = try await SentenceBoundaryAPI.fetch(videoHash: video.videoHash)
                guard activeVideoHash == video.videoHash else {
                    return
                }
                let item = TrainingMediaItem(
                    id: video.videoHash,
                    title: video.title,
                    url: url,
                    sentences: document.sentences
                )
                controller.load(item)
                controller.boundaryState = .loaded(count: document.sentences.count)
                controller.showTransientMessage("课程已就绪：\(document.sentences.count) 句")
            } catch {
                guard activeVideoHash == video.videoHash else {
                    return
                }
                controller.boundaryState = .failed(message: error.localizedDescription)
                controller.showTransientMessage("句子边界加载失败：\(error.localizedDescription)")
            }
        }
    }

    /// 与安卓端 onActivityResult 本地视频流程一致：先装载再按 SHA-256 匹配句边界。
    func importVideo(at url: URL) async {
        guard let controller = controller else {
            return
        }
        let didAccess = url.startAccessingSecurityScopedResource()
        defer {
            if didAccess {
                url.stopAccessingSecurityScopedResource()
            }
        }
        controller.showTransientMessage("正在获取本地视频的句子边界...")
        do {
            let fileName = url.lastPathComponent
            let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            let tempURL = cacheDir.appendingPathComponent("import-\(UUID().uuidString)-\(fileName)")
            let hash = try ImitationAssessor.copyAndHash(from: url, to: tempURL)
            let item = TrainingMediaItem(
                id: hash,
                title: fileName,
                url: tempURL,
                isLocalFile: true
            )
            activeVideoHash = hash
            controller.load(item)
            controller.boundaryState = .loading(isLocal: true)
            do {
                let document = try await SentenceBoundaryAPI.fetch(videoHash: hash)
                guard activeVideoHash == hash else {
                    return
                }
                let loadedItem = TrainingMediaItem(
                    id: hash,
                    title: fileName,
                    url: tempURL,
                    sentences: document.sentences,
                    isLocalFile: true
                )
                controller.load(loadedItem)
                controller.boundaryState = .loaded(count: document.sentences.count)
                controller.showTransientMessage("句子边界已加载：\(document.sentences.count) 句")
            } catch {
                guard activeVideoHash == hash else {
                    return
                }
                controller.boundaryState = .failed(message: error.localizedDescription)
                controller.showTransientMessage("句子边界获取失败，使用默认快退/快进：\(error.localizedDescription)")
            }
        } catch {
            controller.showTransientMessage("本地视频导入失败：\(error.localizedDescription)")
        }
    }
}

struct CatalogView: View {
    @ObservedObject var vm: CatalogViewModel

    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            HStack(spacing: 0) {
                Text("课程")
                    .font(.system(size: 16, weight: .bold))
                    .foregroundColor(.white)
                Spacer()
                Text(vm.statusText)
                    .font(.system(size: 13))
                    .foregroundColor(Theme.textMuted)
                    .lineLimit(2)
                    .multilineTextAlignment(.trailing)
            }
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 10) {
                    ForEach(vm.videos) { video in
                        catalogCard(video)
                    }
                }
                .padding(.vertical, 2)
            }
        }
        .padding(.horizontal, 16)
        .task {
            await vm.loadIfNeeded()
        }
    }

    private func catalogCard(_ video: TrainingVideoSummary) -> some View {
        let selected = vm.activeVideoHash == video.videoHash
        return Button {
            vm.select(video)
        } label: {
            VStack(alignment: .leading, spacing: 6) {
                Text(video.title)
                    .font(.system(size: 15, weight: .bold))
                    .foregroundColor(selected ? Theme.textDark : .white)
                    .lineLimit(2)
                    .multilineTextAlignment(.leading)
                Text("\(video.sentenceCount) 句 · \(Theme.formatTime(video.durationMs ?? -1))")
                    .font(.system(size: 12))
                    .foregroundColor(selected ? Theme.accentDeep : Theme.textMuted)
            }
            .padding(.horizontal, 14)
            .frame(width: 218, height: 84, alignment: .leading)
            .background(selected ? Theme.selected : Theme.surface)
            .overlay(
                RoundedRectangle(cornerRadius: 8)
                    .stroke(selected ? Theme.accent : Theme.border, lineWidth: 1)
            )
            .clipShape(RoundedRectangle(cornerRadius: 8))
        }
        .buttonStyle(.plain)
    }
}
