//
//  ScenicPromotionViewModels.swift
//  suixinkan
//
//  Created by Codex on 2026/7/2.
//

import Combine
import Foundation
import SwiftUI

/// 宣传页主视图 ViewModel，驱动看板数据、打卡点切换与素材选中状态。
@MainActor
final class ScenicPromotionViewModel: ObservableObject {
    @Published private(set) var pageState: ScenicPromotionPageState = .idle
    @Published private(set) var board: ScenicPromotionBoard?
    @Published var selectedSpotID: String?
    @Published var selectedMode: ScenicPromotionMediaMode = .video
    @Published var selectedMediaID: ScenicPromotionMedia.ID?
    @Published private(set) var activeScenario: ScenicPromotionDemoScenario = .normal

    /// 当前选中的打卡点；如果 selectedSpotID 为空，默认取第一个点位。
    var selectedSpot: ScenicPromotionSpot? {
        guard let board else { return nil }
        return board.spots.first { $0.id == selectedSpotID } ?? board.spots.first
    }

    /// 无排队演示状态下返回 nil，让页面展示“当前暂无排队信息”。
    var queue: ScenicPromotionQueueInfo? {
        guard activeScenario != .noQueue else { return nil }
        return selectedSpot?.queue
    }

    /// 当前素材列表随“视频 / 照片”切换变化，缩略图和主视觉共用这组数据。
    var mediaItems: [ScenicPromotionMedia] {
        guard activeScenario != .noMedia else { return [] }
        return Self.mediaItems(for: selectedSpot, mode: selectedMode)
    }

    /// 当前主视觉素材；如果选中 ID 丢失，兜底展示列表第一条。
    var selectedMedia: ScenicPromotionMedia? {
        let items = mediaItems
        return items.first { $0.id == selectedMediaID } ?? items.first
    }

    var hasQueueData: Bool {
        queue != nil
    }

    /// 首次进入页面时加载 Mock 数据。
    func loadIfNeeded() async {
        guard pageState == .idle else { return }
        await load()
    }

    /// 模拟接口加载耗时，便于展示 Loading 状态。
    func load() async {
        withAnimation(.easeInOut(duration: 0.25)) {
            pageState = .loading
        }
        try? await Task.sleep(nanoseconds: 350_000_000)
        guard !Task.isCancelled else { return }
        applyScenario(.normal)
    }

    /// 切换产品演示状态，覆盖正常、无媒体、无排队、加载、异常和空数据。
    func applyScenario(_ scenario: ScenicPromotionDemoScenario) {
        activeScenario = scenario
        withAnimation(.easeInOut(duration: 0.28)) {
            switch scenario {
            case .normal, .noMedia, .noQueue:
                board = ScenicPromotionMockStore.mockBoard
                selectedSpotID = selectedSpotID ?? board?.spots.first?.id
                selectedMediaID = Self.mediaItems(for: selectedSpot, mode: selectedMode).first?.id
                pageState = .normal
            case .loading:
                pageState = .loading
            case .error:
                board = nil
                pageState = .error("宣传页数据加载失败，请稍后重试")
            case .empty:
                board = nil
                pageState = .empty
            }
        }
    }

    /// 切换打卡点时，默认选中当前素材类型下的第一条素材。
    func selectSpot(_ spot: ScenicPromotionSpot) {
        withAnimation(.spring(response: 0.28, dampingFraction: 0.82)) {
            selectedSpotID = spot.id
            selectedMediaID = Self.mediaItems(for: spot, mode: selectedMode).first?.id
        }
    }

    /// 切换视频 / 照片时，底部主视觉同步切到该模式下第一条素材。
    func selectMode(_ mode: ScenicPromotionMediaMode) {
        withAnimation(.spring(response: 0.28, dampingFraction: 0.82)) {
            selectedMode = mode
            selectedMediaID = Self.mediaItems(for: selectedSpot, mode: mode).first?.id
        }
    }

    /// 点击横向素材缩略图，只更新选中态并联动底部主视觉。
    func selectMedia(_ media: ScenicPromotionMedia) {
        withAnimation(.spring(response: 0.28, dampingFraction: 0.82)) {
            selectedMediaID = media.id
        }
    }

    /// 左右箭头循环切换当前素材。
    func moveMedia(_ offset: Int) {
        let items = mediaItems
        guard !items.isEmpty else { return }
        let currentIndex = items.firstIndex { $0.id == selectedMedia?.id } ?? 0
        let nextIndex = (currentIndex + offset + items.count) % items.count
        selectMedia(items[nextIndex])
    }

    /// 根据点位和素材类型取素材列表，避免切换模式时读到旧模式数据。
    static func mediaItems(
        for spot: ScenicPromotionSpot?,
        mode: ScenicPromotionMediaMode
    ) -> [ScenicPromotionMedia] {
        switch mode {
        case .video:
            return spot?.videos ?? []
        case .photo:
            return spot?.photos ?? []
        }
    }
}

/// 宣传页设置 ViewModel，管理排队绑定、素材点、上传、删除和提交。
@MainActor
final class ScenicPromotionSettingsViewModel: ObservableObject {
    @Published private(set) var pageState: ScenicPromotionSettingsPageState = .idle
    @Published private(set) var queuePoints: [ScenicPromotionQueuePoint] = []
    @Published private(set) var materialPoints: [ScenicPromotionMaterialPoint] = []
    @Published var selectedQueuePointIDs: Set<String> = []
    @Published var selectedMaterialPointID: String?
    @Published private(set) var materialsByPointID: [String: [ScenicPromotionManagedMaterial]] = [:]
    /// 当前要预览的已上传素材。
    @Published var previewMaterial: ScenicPromotionManagedMaterial?

    /// 多选排队打卡点的展示文案。
    var selectedQueuePointNames: String {
        let names = queuePoints
            .filter { selectedQueuePointIDs.contains($0.id) }
            .map(\.name)
        return names.isEmpty ? "请选择排队打卡点" : names.joined(separator: "、")
    }

    /// 当前素材上传所属打卡点。
    var selectedMaterialPoint: ScenicPromotionMaterialPoint? {
        guard let selectedMaterialPointID else { return nil }
        return materialPoints.first { $0.id == selectedMaterialPointID }
    }

    /// 素材点选择框的展示文案。
    var selectedMaterialPointName: String {
        selectedMaterialPoint?.name ?? "请选择素材所属打卡点"
    }

    /// 首次进入设置页时加载 Mock 配置。
    func loadIfNeeded() async {
        guard pageState == .idle else { return }
        await load()
    }

    /// 模拟设置页加载过程。
    func load() async {
        withAnimation(.easeInOut(duration: 0.24)) {
            pageState = .loading
        }
        try? await Task.sleep(nanoseconds: 260_000_000)
        guard !Task.isCancelled else { return }
        applyScenario(.normal)
    }

    /// 切换设置页演示状态。
    func applyScenario(_ scenario: ScenicPromotionSettingsDemoScenario) {
        withAnimation(.easeInOut(duration: 0.24)) {
            switch scenario {
            case .normal:
                queuePoints = ScenicPromotionMockStore.mockQueuePoints
                materialPoints = ScenicPromotionMockStore.mockMaterialPoints
                selectedQueuePointIDs = ["queue_bridge", "queue_hendi"]
                selectedMaterialPointID = selectedMaterialPointID ?? ScenicPromotionMockStore.mockMaterialPoints.first?.id
                materialsByPointID = ScenicPromotionMockStore.mockMaterialsByPointID
                pageState = .normal
            case .loading:
                pageState = .loading
            case .empty:
                queuePoints = []
                materialPoints = []
                selectedQueuePointIDs = []
                selectedMaterialPointID = nil
                materialsByPointID = [:]
                pageState = .empty
            case .error:
                queuePoints = []
                materialPoints = []
                selectedQueuePointIDs = []
                selectedMaterialPointID = nil
                materialsByPointID = [:]
                pageState = .error("宣传页设置数据加载失败，请稍后重试")
            }
        }
    }

    /// 排队打卡点多选逻辑。
    func toggleQueuePoint(_ point: ScenicPromotionQueuePoint) {
        withAnimation(.spring(response: 0.25, dampingFraction: 0.86)) {
            if selectedQueuePointIDs.contains(point.id) {
                selectedQueuePointIDs.remove(point.id)
            } else {
                selectedQueuePointIDs.insert(point.id)
            }
        }
    }

    /// 切换素材所属打卡点后，页面会展示该点位已有素材。
    func selectMaterialPoint(_ point: ScenicPromotionMaterialPoint) {
        withAnimation(.spring(response: 0.25, dampingFraction: 0.86)) {
            selectedMaterialPointID = point.id
        }
    }

    /// 获取当前素材点下指定类型的素材。
    func materials(for kind: ScenicPromotionUploadKind) -> [ScenicPromotionManagedMaterial] {
        guard let selectedMaterialPointID else { return [] }
        return (materialsByPointID[selectedMaterialPointID] ?? [])
            .filter { $0.kind == kind }
    }

    /// 模拟上传素材；未选择素材点时返回 false，由页面弹提示。
    func addMaterial(kind: ScenicPromotionUploadKind) -> Bool {
        guard let point = selectedMaterialPoint else { return false }
        let currentItems = materialsByPointID[point.id] ?? []
        let nextIndex = currentItems.filter { $0.kind == kind }.count + 1
        let assetName = ScenicPromotionMockStore.assetName(for: kind, index: nextIndex)
        let material = ScenicPromotionManagedMaterial(
            id: "\(point.id)_\(kind.rawValue)_\(UUID().uuidString)",
            pointID: point.id,
            kind: kind,
            title: "\(point.name)\(kind.shortTitle)\(nextIndex)",
            subtitle: kind == .video ? "模拟上传视频，待后续接入真实素材" : "模拟上传图片，待后续接入真实素材",
            assetName: assetName,
            duration: kind == .video ? "00:\(String(format: "%02d", 20 + nextIndex * 3))" : nil,
            createdAt: "2026-06-25 19:\(String(format: "%02d", 20 + nextIndex))"
        )

        withAnimation(.spring(response: 0.28, dampingFraction: 0.86)) {
            materialsByPointID[point.id, default: []].append(material)
        }
        return true
    }

    /// 删除当前素材点下的某个素材。
    func deleteMaterial(_ material: ScenicPromotionManagedMaterial) {
        withAnimation(.spring(response: 0.25, dampingFraction: 0.86)) {
            materialsByPointID[material.pointID]?.removeAll { $0.id == material.id }
        }
    }

    /// 打开素材预览页。
    func openMaterialPreview(_ material: ScenicPromotionManagedMaterial) {
        previewMaterial = material
    }

    /// 提交前校验至少绑定一个排队打卡点。
    func submit() -> Bool {
        guard !selectedQueuePointIDs.isEmpty else { return false }
        return true
    }
}
