//
//  LiveViewModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/25.
//

import Foundation
import Combine

@MainActor
/// 直播管理 ViewModel，负责直播列表、详情、创建和控制动作。
final class LiveManagementViewModel: ObservableObject {
    @Published var items: [LiveEntity] = []
    @Published var detail: LiveEntity?
    @Published var loading = false
    @Published var loadingMore = false
    @Published var hasMore = false
    @Published var page = 1
    @Published var errorMessage: String?

    private let pageSize = 10
    @Published private var total = 0

    /// 进行中的直播数量。
    var liveRunningCount: Int {
        items.filter { $0.status == 2 }.count
    }

    /// 已结束的直播数量。
    var liveFinishedCount: Int {
        items.filter { $0.status == 3 }.count
    }

    /// 重新加载直播列表。
    func reload(api: any LiveServing, scenicId: Int?, showLoading: Bool = true) async {
        guard let scenicId else {
            reset()
            return
        }
        if showLoading { loading = true }
        errorMessage = nil
        defer { loading = false }
        do {
            try await loadPage(api: api, scenicId: scenicId, page: 1)
        } catch {
            clearListAndDetail()
            errorMessage = error.localizedDescription
        }
    }

    /// 加载下一页直播列表。
    func loadMore(api: any LiveServing, scenicId: Int?) async {
        guard let scenicId, hasMore, !loadingMore else { return }
        loadingMore = true
        defer { loadingMore = false }
        do {
            try await loadPage(api: api, scenicId: scenicId, page: page + 1)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 创建直播并刷新列表。
    func create(api: any LiveServing, scenicId: Int?, title: String, coverURL: String) async throws {
        guard let scenicId else {
            throw LiveValidationError.missingScenic
        }
        let normalizedTitle = title.liveTrimmed
        let normalizedCover = coverURL.liveTrimmed
        guard !normalizedTitle.isEmpty else {
            throw LiveValidationError.emptyTitle
        }
        guard normalizedCover.liveIsHTTPURL else {
            throw LiveValidationError.invalidCoverURL
        }
        try await api.liveCreate(LiveCreateRequest(scenicId: String(scenicId), title: normalizedTitle, coverImg: normalizedCover))
        await reload(api: api, scenicId: scenicId, showLoading: false)
    }

    /// 加载直播详情，失败时清空旧详情。
    func loadDetail(api: any LiveServing, liveId: Int) async {
        do {
            detail = try await api.liveDetail(liveId: liveId)
        } catch {
            detail = nil
            errorMessage = error.localizedDescription
        }
    }

    /// 开始或暂停直播，并刷新列表。
    func control(api: any LiveServing, item: LiveEntity, scenicId: Int?) async throws {
        if item.status == 2 {
            try await api.liveStop(liveId: item.id)
        } else if item.status != 3 {
            try await api.liveStart(liveId: item.id)
        }
        await reload(api: api, scenicId: scenicId, showLoading: false)
    }

    /// 结束直播并刷新列表。
    func finish(api: any LiveServing, item: LiveEntity, scenicId: Int?) async throws {
        try await api.liveFinish(liveId: item.id)
        await reload(api: api, scenicId: scenicId, showLoading: false)
    }

    private func loadPage(api: any LiveServing, scenicId: Int, page: Int) async throws {
        let response = try await api.liveList(scenicId: scenicId, page: page, pageSize: pageSize)
        if page == 1 {
            items = response.items
        } else {
            let incomingById = Dictionary(uniqueKeysWithValues: response.items.map { ($0.id, $0) })
            let kept = items.filter { incomingById[$0.id] == nil }
            items = kept + response.items
        }
        self.page = page
        total = response.total
        hasMore = items.count < total
    }

    private func reset() {
        clearListAndDetail()
        loading = false
        loadingMore = false
        errorMessage = nil
    }

    private func clearListAndDetail() {
        items = []
        detail = nil
        page = 1
        total = 0
        hasMore = false
    }
}

@MainActor
/// 直播详情 ViewModel，负责直播详情页动作后的详情刷新。
final class LiveDetailViewModel: ObservableObject {
    @Published var detail: LiveEntity
    @Published var loading = false
    @Published var actionInFlight = false
    @Published var errorMessage: String?

    init(detail: LiveEntity) {
        self.detail = detail
    }

    /// 刷新直播详情。
    func refresh(api: any LiveServing, showLoading: Bool = true) async {
        if showLoading { loading = true }
        defer { loading = false }
        do {
            detail = try await api.liveDetail(liveId: detail.id)
        } catch {
            errorMessage = error.localizedDescription
        }
    }

    /// 开始或暂停当前直播。
    func control(api: any LiveServing) async throws {
        guard detail.status != 3 else { return }
        actionInFlight = true
        defer { actionInFlight = false }
        if detail.status == 2 {
            try await api.liveStop(liveId: detail.id)
        } else {
            try await api.liveStart(liveId: detail.id)
        }
        await refresh(api: api, showLoading: false)
    }

    /// 结束当前直播。
    func finish(api: any LiveServing) async throws {
        guard detail.status != 3 else { return }
        actionInFlight = true
        defer { actionInFlight = false }
        try await api.liveFinish(liveId: detail.id)
        await refresh(api: api, showLoading: false)
    }

    /// 切换推流模式。
    func setPushMode(api: any LiveServing, mode: Int) async throws {
        guard detail.manualPushMode != mode else { return }
        actionInFlight = true
        defer { actionInFlight = false }
        try await api.liveSetPushMode(liveId: detail.id, mode: mode)
        await refresh(api: api, showLoading: false)
    }
}

@MainActor
/// 直播相册 ViewModel，负责相册列表、筛选、新建和删除。
final class LiveAlbumViewModel: ObservableObject {
    @Published var folders: [LiveAlbumFolderItem] = []
    @Published var startDate: Date?
    @Published var endDate: Date?
    @Published var loading = false
    @Published var loadingMore = false
    @Published var hasMore = false
    @Published var page = 1
    @Published var errorMessage: String?

    private let pageSize = 10
    @Published private var total = 0

    /// 设置开始时间并校验时间顺序。
    func setStartDate(_ date: Date) throws {
        if let endDate, Calendar.current.startOfDay(for: date) > Calendar.current.startOfDay(for: endDate) {
            throw LiveValidationError.invalidDateRange
        }
        startDate = date
    }

    /// 设置结束时间并校验时间顺序。
    func setEndDate(_ date: Date) throws {
        if let startDate, Calendar.current.startOfDay(for: date) < Calendar.current.startOfDay(for: startDate) {
            throw LiveValidationError.invalidDateRange
        }
        endDate = date
    }

    /// 清除日期筛选。
    func clearDateFilters() {
        startDate = nil
        endDate = nil
    }

    /// 重新加载直播相册列表。
    func reload(api: any LiveServing, scenicId: Int?, showLoading: Bool = true) async {
        guard let scenicId else {
            reset()
            return
        }
        if showLoading { loading = true }
        errorMessage = nil
        defer { loading = false }
        do {
            try await loadPage(api: api, scenicId: scenicId, page: 1)
        } catch {
            clearFolders()
            errorMessage = error.localizedDescription
        }
    }

    /// 加载下一页直播相册。
    func loadMore(api: any LiveServing, scenicId: Int?) async {
        guard let scenicId, hasMore, !loadingMore else { return }
        loadingMore = true
        let nextPage = page + 1
        defer { loadingMore = false }
        do {
            try await loadPage(api: api, scenicId: scenicId, page: nextPage)
        } catch {
            page = max(1, nextPage - 1)
            errorMessage = error.localizedDescription
        }
    }

    /// 删除直播相册并刷新。
    func deleteFolder(api: any LiveServing, folderId: Int, scenicId: Int?) async throws {
        try await api.liveAlbumDeleteFolder(folderId: folderId)
        await reload(api: api, scenicId: scenicId, showLoading: false)
    }

    private func loadPage(api: any LiveServing, scenicId: Int, page: Int) async throws {
        let response = try await api.liveAlbumList(
            scenicId: scenicId,
            startTime: startDate?.liveDayText,
            endTime: endDate?.liveDayText,
            page: page,
            pageSize: pageSize
        )
        if page == 1 {
            folders = response.items
        } else {
            let incomingById = Set(response.items.map(\.id))
            folders = folders.filter { !incomingById.contains($0.id) } + response.items
        }
        self.page = page
        total = response.total
        hasMore = folders.count < total
    }

    private func reset() {
        clearFolders()
        loading = false
        loadingMore = false
        errorMessage = nil
    }

    private func clearFolders() {
        folders = []
        page = 1
        total = 0
        hasMore = false
    }
}

@MainActor
/// 新建直播相册 ViewModel，负责本地素材上传和创建相册。
final class LiveAlbumCreateViewModel: ObservableObject {
    @Published var name = ""
    @Published var localFiles: [LiveAlbumLocalUploadFile] = []
    @Published var submitting = false
    @Published var uploadProgress = 0
    @Published var errorMessage: String?

    /// 添加本地待上传素材。
    func addLocalFiles(_ files: [LiveAlbumLocalUploadFile]) {
        localFiles.append(contentsOf: files)
    }

    /// 删除本地待上传素材。
    func removeLocalFile(id: UUID) {
        localFiles.removeAll { $0.id == id }
    }

    /// 上传本地素材并创建直播相册。
    func submit(scenicId: Int?, api: any LiveServing, uploader: any OSSUploadServing) async throws {
        guard let scenicId else { throw LiveValidationError.missingScenic }
        let normalizedName = name.liveTrimmed
        guard !normalizedName.isEmpty else { throw LiveValidationError.emptyAlbumName }
        guard !localFiles.isEmpty else { throw LiveValidationError.emptyAlbumFiles }
        guard !submitting else { return }

        submitting = true
        uploadProgress = 0
        defer { submitting = false }

        var uploadedItems: [LiveAlbumCreateFileItem] = []
        let count = max(localFiles.count, 1)
        do {
            for index in localFiles.indices {
                let file = localFiles[index]
                let url = try await uploader.uploadAliveAlbumFile(
                    data: file.data,
                    fileName: file.fileName,
                    fileType: file.fileType,
                    scenicId: scenicId
                ) { progress in
                    Task { @MainActor in
                        let base = Double(index) / Double(count)
                        let step = Double(progress) / Double(count)
                        self.uploadProgress = min(99, Int((base + step / 100) * 100))
                    }
                }
                localFiles[index].uploadedURL = url
                uploadedItems.append(
                    LiveAlbumCreateFileItem(url: url, type: file.fileType, size: file.size, coverImg: nil)
                )
            }
            try await api.liveAlbumCreateFolder(
                LiveAlbumCreateFolderRequest(scenicId: String(scenicId), name: normalizedName, items: uploadedItems)
            )
            name = ""
            localFiles = []
            uploadProgress = 100
        } catch {
            errorMessage = error.localizedDescription
            throw error
        }
    }
}

@MainActor
/// 直播相册预览 ViewModel，负责加载相册详情和删除当前素材。
final class LiveAlbumPreviewViewModel: ObservableObject {
    let folderId: Int
    @Published var folder: LiveAlbumFolderItem?
    @Published var files: [LiveAlbumFileItem] = []
    @Published var currentIndex: Int
    @Published var loading = false
    @Published var errorMessage: String?

    init(folderId: Int, startIndex: Int = 0, summary: LiveAlbumFolderItem? = nil) {
        self.folderId = folderId
        currentIndex = max(startIndex, 0)
        folder = summary
        files = summary?.items ?? []
    }

    /// 加载相册详情。
    func load(api: any LiveServing) async {
        loading = true
        defer { loading = false }
        do {
            let detail = try await api.liveAlbumFolderDetail(folderId: folderId)
            folder = detail
            files = detail.items
            if currentIndex >= files.count {
                currentIndex = max(files.count - 1, 0)
            }
        } catch {
            files = []
            errorMessage = error.localizedDescription
        }
    }

    /// 删除当前预览素材并刷新详情。
    func deleteCurrentFile(api: any LiveServing) async throws {
        guard files.indices.contains(currentIndex) else { return }
        let file = files[currentIndex]
        try await api.liveAlbumDeleteFiles(folderId: folderId, fileIds: [file.id])
        await load(api: api)
    }
}

/// 直播模块校验错误。
enum LiveValidationError: LocalizedError, Equatable {
    case missingScenic
    case emptyTitle
    case invalidCoverURL
    case invalidDateRange
    case emptyAlbumName
    case emptyAlbumFiles

    var errorDescription: String? {
        switch self {
        case .missingScenic:
            "当前账号缺少景区信息"
        case .emptyTitle:
            "请输入直播标题"
        case .invalidCoverURL:
            "封面图地址需以 http:// 或 https:// 开头"
        case .invalidDateRange:
            "开始时间不能大于结束时间"
        case .emptyAlbumName:
            "请输入相册名称"
        case .emptyAlbumFiles:
            "请上传素材"
        }
    }
}

extension Date {
    /// 直播接口使用的日期格式。
    var liveDayText: String {
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy-MM-dd"
        return formatter.string(from: self)
    }
}

private extension String {
    var liveIsHTTPURL: Bool {
        let lower = liveTrimmed.lowercased()
        return lower.hasPrefix("http://") || lower.hasPrefix("https://")
    }
}

extension Int64 {
    /// 直播时长展示文案。
    var liveDurationText: String {
        let totalSeconds = Swift.max(Int(self), 0)
        let hours = totalSeconds / 3600
        let minutes = (totalSeconds % 3600) / 60
        let seconds = totalSeconds % 60
        return "\(hours):\(String(format: "%02d", minutes)):\(String(format: "%02d", seconds))"
    }
}
