//
//  ScenicPromotionTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/7/2.
//

import XCTest
@testable import suixinkan

/// 宣传页主页面 ViewModel 测试，覆盖加载、场景切换与素材选中逻辑。
@MainActor
final class ScenicPromotionViewModelTests: XCTestCase {
    /// 测试正常场景加载后进入 normal 状态并选中首个打卡点与素材。
    func testApplyNormalScenarioLoadsBoardAndSelection() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)

        XCTAssertEqual(viewModel.pageState, .normal)
        XCTAssertNotNil(viewModel.board)
        XCTAssertEqual(viewModel.board?.scenicName, "那拉提景区")
        XCTAssertNotNil(viewModel.selectedSpot)
        XCTAssertNotNil(viewModel.selectedMedia)
        XCTAssertFalse(viewModel.mediaItems.isEmpty)
    }

    /// 测试空数据场景清空看板并进入 empty 状态。
    func testApplyEmptyScenarioClearsBoard() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)
        viewModel.applyScenario(.empty)

        XCTAssertEqual(viewModel.pageState, .empty)
        XCTAssertNil(viewModel.board)
    }

    /// 测试异常场景进入 error 状态。
    func testApplyErrorScenarioSetsErrorState() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.error)

        if case .error = viewModel.pageState {
            XCTAssertTrue(true)
        } else {
            XCTFail("Expected error page state")
        }
    }

    /// 测试无媒体场景清空素材列表。
    func testNoMediaScenarioHidesMediaItems() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)
        viewModel.applyScenario(.noMedia)

        XCTAssertTrue(viewModel.mediaItems.isEmpty)
        XCTAssertNil(viewModel.selectedMedia)
    }

    /// 测试无排队场景隐藏排队信息。
    func testNoQueueScenarioHidesQueueInfo() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)
        viewModel.applyScenario(.noQueue)

        XCTAssertNil(viewModel.queue)
        XCTAssertFalse(viewModel.hasQueueData)
    }

    /// 测试切换打卡点与素材模式会更新选中素材。
    func testSelectSpotAndModeUpdatesSelectedMedia() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)

        guard let board = viewModel.board, board.spots.count > 1 else {
            XCTFail("Expected multiple spots in mock board")
            return
        }

        let secondSpot = board.spots[1]
        viewModel.selectSpot(secondSpot)
        XCTAssertEqual(viewModel.selectedSpot?.id, secondSpot.id)

        viewModel.selectMode(.photo)
        XCTAssertEqual(viewModel.selectedMode, .photo)
        XCTAssertEqual(viewModel.selectedMedia?.mode, .photo)
    }

    /// 测试左右切换素材循环选中。
    func testMoveMediaCyclesSelection() {
        let viewModel = ScenicPromotionViewModel()
        viewModel.applyScenario(.normal)

        guard viewModel.mediaItems.count > 1 else {
            XCTFail("Expected multiple media items")
            return
        }

        let firstID = viewModel.selectedMedia?.id
        viewModel.moveMedia(1)
        XCTAssertNotEqual(viewModel.selectedMedia?.id, firstID)

        viewModel.moveMedia(-1)
        XCTAssertEqual(viewModel.selectedMedia?.id, firstID)
    }
}

/// 宣传页设置 ViewModel 测试，覆盖上传校验与提交规则。
@MainActor
final class ScenicPromotionSettingsViewModelTests: XCTestCase {
    /// 测试正常场景加载 Mock 配置数据。
    func testApplyNormalScenarioLoadsSettingsData() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)

        XCTAssertEqual(viewModel.pageState, .normal)
        XCTAssertFalse(viewModel.queuePoints.isEmpty)
        XCTAssertFalse(viewModel.materialPoints.isEmpty)
        XCTAssertFalse(viewModel.selectedQueuePointIDs.isEmpty)
    }

    /// 测试未选择素材点时上传会被拦截。
    func testAddMaterialFailsWithoutMaterialPoint() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)
        viewModel.selectedMaterialPointID = nil

        XCTAssertFalse(viewModel.addMaterial(kind: .video))
    }

    /// 测试选择素材点后可以 Mock 上传素材。
    func testAddMaterialSucceedsWithMaterialPoint() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)

        guard let point = viewModel.materialPoints.first else {
            XCTFail("Expected material points")
            return
        }

        viewModel.selectMaterialPoint(point)
        let beforeCount = viewModel.materials(for: .video).count
        XCTAssertTrue(viewModel.addMaterial(kind: .video))
        XCTAssertEqual(viewModel.materials(for: .video).count, beforeCount + 1)
    }

    /// 测试删除素材会从当前点位列表移除。
    func testDeleteMaterialRemovesItem() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)

        guard let point = viewModel.materialPoints.first else {
            XCTFail("Expected material points")
            return
        }

        viewModel.selectMaterialPoint(point)
        guard viewModel.addMaterial(kind: .image),
              let material = viewModel.materials(for: .image).last else {
            XCTFail("Expected uploaded material")
            return
        }

        viewModel.deleteMaterial(material)
        XCTAssertFalse(viewModel.materials(for: .image).contains(where: { $0.id == material.id }))
    }

    /// 测试未绑定排队点时提交失败。
    func testSubmitFailsWithoutQueuePoints() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)
        viewModel.selectedQueuePointIDs = []

        XCTAssertFalse(viewModel.submit())
    }

    /// 测试绑定排队点后提交成功。
    func testSubmitSucceedsWithQueuePoints() {
        let viewModel = ScenicPromotionSettingsViewModel()
        viewModel.applyScenario(.normal)

        XCTAssertTrue(viewModel.submit())
    }
}
