//
//  LiveViewModels.swift
//  suixinkan
//

import Foundation

/// 直播 OSS 上传协议，便于直播 ViewModel 在测试中注入替身。
@MainActor
protocol LiveOSSUploading {
    /// 上传直播封面并返回 OSS URL。
    func uploadLiveCover(data: Data, fileName: String, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String

    /// 上传直播相册素材并返回 OSS URL。
    func uploadLiveAlbumFile(data: Data, fileName: String, fileType: Int, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String
}

extension OSSUploadService: LiveOSSUploading {}

/// 直播列表页 ViewModel，负责分页、新建和直播控制。
final class LiveManageViewModel {
    private(set) var items: [LiveItem] = []
    private(set) var isLoading = false
    private(set) var isRefreshing = false
    private(set) var canLoadMore = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?
    var onLiveCreated: (() -> Void)?

    private let currentScenicIdProvider: () -> Int
    private var currentPage = 1
    private var totalCount = 0
    private let pageSize = 10

    /// 初始化直播管理 ViewModel。
    init(currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.session.currentScenicId }) {
        self.currentScenicIdProvider = currentScenicIdProvider
    }

    /// 首次加载直播列表。
    func loadInitial(api: any LiveServing) async {
        await load(reset: true, showLoading: true, api: api)
    }

    /// 下拉刷新直播列表。
    func refresh(api: any LiveServing) async {
        isRefreshing = true
        notifyStateChange()
        await load(reset: true, showLoading: false, api: api)
    }

    /// 滚动到底部附近时加载更多。
    func loadMoreIfNeeded(lastVisibleIndex: Int, api: any LiveServing) async {
        guard lastVisibleIndex >= items.count - 3 else { return }
        await load(reset: false, showLoading: false, api: api)
    }

    /// 新建直播并刷新第一页。
    func createLive(title: String, coverUrl: String, api: any LiveServing) async {
        let name = title.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !name.isEmpty else {
            onShowMessage?("请输入直播标题")
            return
        }
        guard !coverUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            onShowMessage?("请上传封面图片")
            return
        }
        guard let scenicId = scenicIdText() else { return }
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await api.createLive(scenicId: scenicId, title: name, coverImg: coverUrl)
            onLiveCreated?()
            await load(reset: true, showLoading: true, api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "新增直播失败" : error.localizedDescription)
        }
    }

    /// 复制直播地址。
    func copyPushURL(_ item: LiveItem) -> String {
        item.pushUrl
    }

    /// 根据当前直播状态执行开始或暂停。
    func controlLive(_ item: LiveItem, api: any LiveServing) async {
        guard item.liveStatus.isOperable else { return }
        await performAndReload(api: api) {
            if item.liveStatus == .living {
                try await api.stopLive(liveId: item.id)
            } else if item.liveStatus == .pending || item.liveStatus == .paused {
                try await api.startLive(liveId: item.id)
            }
        }
    }

    /// 结束直播。
    func finishLive(_ item: LiveItem, api: any LiveServing) async {
        guard item.liveStatus.isOperable else { return }
        await performAndReload(api: api) {
            try await api.finishLive(liveId: item.id)
        }
    }

    private func load(reset: Bool, showLoading: Bool, api: any LiveServing) async {
        guard let scenicId = scenicIdText() else { return }
        if reset {
            currentPage = 1
            canLoadMore = false
        } else {
            guard canLoadMore, !isLoading else { return }
            currentPage += 1
        }
        isLoading = showLoading
        notifyStateChange()
        defer {
            isLoading = false
            isRefreshing = false
            notifyStateChange()
        }
        do {
            let response = try await api.liveList(scenicId: scenicId, page: currentPage, pageSize: pageSize)
            totalCount = response.total
            items = reset ? response.items : items + response.items
            canLoadMore = items.count < totalCount
        } catch is CancellationError {
            return
        } catch {
            if !reset, currentPage > 1 { currentPage -= 1 }
            if reset {
                items = []
                totalCount = 0
                canLoadMore = false
            }
            onShowMessage?(error.localizedDescription.isEmpty ? "获取直播列表失败" : error.localizedDescription)
        }
    }

    private func performAndReload(api: any LiveServing, operation: () async throws -> Void) async {
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await operation()
            await load(reset: true, showLoading: true, api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "操作失败" : error.localizedDescription)
        }
    }

    private func scenicIdText() -> String? {
        let scenicId = currentScenicIdProvider()
        guard scenicId > 0 else {
            onShowMessage?("请先选择景区")
            return nil
        }
        return String(scenicId)
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

/// 直播详情页 ViewModel，负责详情加载、控制和推流模式切换。
final class LiveDetailViewModel {
    private(set) var item: LiveItem?
    private(set) var pushMode: LivePushMode = .clarityFirst
    private(set) var isLoading = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?

    let liveId: Int

    /// 初始化直播详情 ViewModel。
    init(liveId: Int) {
        self.liveId = liveId
    }

    /// 加载直播详情。
    func load(api: any LiveServing) async {
        guard liveId > 0 else {
            onShowMessage?("直播ID无效")
            return
        }
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            let detail = try await api.liveDetail(liveId: liveId)
            item = detail
            pushMode = LivePushMode(rawValue: detail.manualPushMode) ?? .clarityFirst
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "获取直播信息失败" : error.localizedDescription)
        }
    }

    /// 设置推流模式。
    func changeMode(_ mode: LivePushMode, api: any LiveServing) async {
        guard pushMode != mode, item?.liveStatus.isOperable == true else { return }
        await performAndReload(api: api) {
            try await api.setPushMode(liveId: liveId, mode: mode.rawValue)
        }
    }

    /// 根据当前直播状态执行开始或暂停。
    func controlLive(api: any LiveServing) async {
        guard let item, item.liveStatus.isOperable else { return }
        await performAndReload(api: api) {
            if item.liveStatus == .living {
                try await api.stopLive(liveId: item.id)
            } else if item.liveStatus == .pending || item.liveStatus == .paused {
                try await api.startLive(liveId: item.id)
            }
        }
    }

    /// 结束直播。
    func finishLive(api: any LiveServing) async {
        guard let item, item.liveStatus.isOperable else { return }
        await performAndReload(api: api) {
            try await api.finishLive(liveId: item.id)
        }
    }

    private func performAndReload(api: any LiveServing, operation: () async throws -> Void) async {
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await operation()
            await load(api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "操作失败" : error.localizedDescription)
        }
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

/// 直播相册列表页 ViewModel，负责日期筛选、分页和文件夹删除。
final class LiveAlbumListViewModel {
    private(set) var folders: [LiveAlbumFolder] = []
    private(set) var startDate: Date?
    private(set) var endDate: Date?
    private(set) var isLoading = false
    private(set) var isRefreshing = false
    private(set) var canLoadMore = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?

    private let currentScenicIdProvider: () -> Int
    private var currentPage = 1
    private var totalCount = 0
    private let pageSize = 10

    /// 初始化直播相册列表 ViewModel。
    init(currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.session.currentScenicId }) {
        self.currentScenicIdProvider = currentScenicIdProvider
    }

    /// 首次加载相册列表。
    func loadInitial(api: any LiveServing) async {
        await load(reset: true, showLoading: true, api: api)
    }

    /// 下拉刷新相册列表。
    func refresh(api: any LiveServing) async {
        isRefreshing = true
        notifyStateChange()
        await load(reset: true, showLoading: false, api: api)
    }

    /// 滚动到底部附近时加载更多。
    func loadMoreIfNeeded(lastVisibleIndex: Int, api: any LiveServing) async {
        guard lastVisibleIndex >= folders.count - 3 else { return }
        await load(reset: false, showLoading: false, api: api)
    }

    /// 更新开始时间。
    func setStartDate(_ date: Date, api: any LiveServing) async {
        if let endDate, date.startOfDay > endDate.startOfDay {
            onShowMessage?("开始时间不能大于结束时间")
            return
        }
        startDate = date.startOfDay
        notifyStateChange()
        await load(reset: true, showLoading: false, api: api)
    }

    /// 更新时间结束。
    func setEndDate(_ date: Date, api: any LiveServing) async {
        if let startDate, date.startOfDay < startDate.startOfDay {
            onShowMessage?("结束时间不能小于开始时间")
            return
        }
        endDate = date.startOfDay
        notifyStateChange()
        await load(reset: true, showLoading: false, api: api)
    }

    /// 删除相册文件夹。
    func deleteFolder(_ folder: LiveAlbumFolder, api: any LiveServing) async {
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await api.deleteAlbumFolder(folderId: folder.id)
            await load(reset: true, showLoading: true, api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "删除相册失败" : error.localizedDescription)
        }
    }

    private func load(reset: Bool, showLoading: Bool, api: any LiveServing) async {
        guard let scenicId = scenicIdText() else { return }
        if reset {
            currentPage = 1
            canLoadMore = false
        } else {
            guard canLoadMore, !isLoading else { return }
            currentPage += 1
        }
        isLoading = showLoading
        notifyStateChange()
        defer {
            isLoading = false
            isRefreshing = false
            notifyStateChange()
        }
        do {
            let response = try await api.albumList(
                scenicId: scenicId,
                startTime: startDate.map(Self.dateString),
                endTime: endDate.map(Self.dateString),
                page: currentPage,
                pageSize: pageSize
            )
            totalCount = response.total
            folders = reset ? response.items : folders + response.items
            canLoadMore = folders.count < totalCount
        } catch is CancellationError {
            return
        } catch {
            if !reset, currentPage > 1 { currentPage -= 1 }
            if reset {
                folders = []
                totalCount = 0
                canLoadMore = false
            }
            onShowMessage?(error.localizedDescription.isEmpty ? "获取直播相册失败" : error.localizedDescription)
        }
    }

    private func scenicIdText() -> String? {
        let scenicId = currentScenicIdProvider()
        guard scenicId > 0 else {
            onShowMessage?("请先选择景区")
            return nil
        }
        return String(scenicId)
    }

    static func dateString(_ date: Date) -> String {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.string(from: date)
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

/// 直播相册上传页 ViewModel，负责本地素材上传和创建相册。
final class LiveAlbumAddViewModel {
    private(set) var title = ""
    private(set) var files: [LiveAlbumFile] = []
    private(set) var isLoading = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?
    var onCreateSuccess: (() -> Void)?

    private let currentScenicIdProvider: () -> Int

    /// 初始化直播相册上传 ViewModel。
    init(currentScenicIdProvider: @escaping () -> Int = { AppStore.shared.session.currentScenicId }) {
        self.currentScenicIdProvider = currentScenicIdProvider
    }

    /// 更新作品名称，最多保留 20 个字符。
    func updateTitle(_ title: String) {
        self.title = String(title.prefix(20))
        notifyStateChange()
    }

    /// 上传本地素材并追加到列表。
    func addLocalMedia(_ media: LivePickedMedia, uploader: any LiveOSSUploading) async {
        guard let scenicId = scenicId() else { return }
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            let url = try await uploader.uploadLiveAlbumFile(
                data: media.data,
                fileName: media.fileName,
                fileType: 2,
                scenicId: scenicId,
                onProgress: { _ in }
            )
            files.append(LiveAlbumFile(url: url, type: 2, size: media.size, coverImg: ""))
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "上传失败" : error.localizedDescription)
        }
    }

    /// 删除本地已上传素材。
    func deleteLocalFile(at index: Int) {
        guard files.indices.contains(index) else { return }
        files.remove(at: index)
        notifyStateChange()
    }

    /// 创建直播相册。
    func create(api: any LiveServing) async {
        let name = title.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !name.isEmpty else {
            onShowMessage?("请输入作品名称")
            return
        }
        guard !files.isEmpty else {
            onShowMessage?("请上传素材")
            return
        }
        guard let scenicId = scenicId().map(String.init) else { return }
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await api.createAlbumFolder(scenicId: scenicId, name: name, items: files)
            onCreateSuccess?()
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "创建相册失败" : error.localizedDescription)
        }
    }

    private func scenicId() -> Int? {
        let scenicId = currentScenicIdProvider()
        guard scenicId > 0 else {
            onShowMessage?("请先选择景区")
            return nil
        }
        return scenicId
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

/// 直播相册预览页 ViewModel，负责详情加载、翻页和删除当前素材。
final class LiveAlbumPreviewViewModel {
    private(set) var files: [LiveAlbumFile] = []
    private(set) var currentIndex: Int
    private(set) var isLoading = false

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?

    let folderId: Int

    /// 初始化直播相册预览 ViewModel。
    init(folderId: Int, startIndex: Int) {
        self.folderId = folderId
        self.currentIndex = max(startIndex, 0)
    }

    /// 加载相册详情。
    func load(api: any LiveServing) async {
        guard folderId > 0 else {
            onShowMessage?("相册ID无效")
            return
        }
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            let detail = try await api.albumFolderDetail(folderId: folderId)
            files = detail.items
            currentIndex = files.isEmpty ? 0 : min(currentIndex, files.count - 1)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "获取相册详情失败" : error.localizedDescription)
        }
    }

    /// 更新当前预览位置。
    func updateCurrentIndex(_ index: Int) {
        currentIndex = max(0, min(index, max(files.count - 1, 0)))
        notifyStateChange()
    }

    /// 删除当前素材并重新加载详情。
    func deleteCurrentFile(api: any LiveServing) async {
        guard files.indices.contains(currentIndex) else { return }
        let file = files[currentIndex]
        isLoading = true
        notifyStateChange()
        defer {
            isLoading = false
            notifyStateChange()
        }
        do {
            try await api.deleteAlbumFile(folderId: folderId, fileId: file.id)
            currentIndex = 0
            await load(api: api)
        } catch is CancellationError {
            return
        } catch {
            onShowMessage?(error.localizedDescription.isEmpty ? "删除素材失败" : error.localizedDescription)
        }
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}

private extension Date {
    var startOfDay: Date {
        Calendar.current.startOfDay(for: self)
    }
}
