//
//  StatisticsViewModelTests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

@MainActor
/// 数据统计 ViewModel 测试。
final class StatisticsViewModelTests: XCTestCase {

    override func setUp() {
        super.setUp()
        AppStore.shared.logout()
        AppStore.shared.session.configureTestAccount(userId: "statistics-user", scenicId: 10)
    }

    override func tearDown() {
        AppStore.shared.logout()
        super.tearDown()
    }

    func testStatisticsSummaryDecodesRefundAmountAsString() throws {
        let json = """
        {
          "code": 100000,
          "msg": "success",
          "data": {
            "order_amount_sum": "0.00",
            "order_count": 0,
            "order_price_avg": "0.00",
            "received_amount_sum": "0.00",
            "refund_amount_sum": "0.00"
          }
        }
        """.data(using: .utf8)!

        let envelope = try JSONDecoder().decode(APIEnvelope<StatisticsSummaryResponse>.self, from: json)
        XCTAssertEqual(envelope.data?.refundAmountSum, "0.00")
        XCTAssertEqual(envelope.data?.orderAmountSum, "0.00")
    }

    func testRangeValueMapping() {
        let viewModel = StatisticsViewModel()
        XCTAssertEqual(viewModel.rangeValue(for: "今日"), "1")
        XCTAssertEqual(viewModel.rangeValue(for: "昨日"), "2")
        XCTAssertEqual(viewModel.rangeValue(for: "7日"), "3")
        XCTAssertEqual(viewModel.rangeValue(for: "本月"), "4")
    }

    func testPeriodButtonCapsuleCornerRadiusUsesHalfHeight() {
        let button = StatisticsPeriodButton(frame: CGRect(x: 0, y: 0, width: 80, height: 32))

        button.setNeedsLayout()
        button.layoutIfNeeded()

        XCTAssertEqual(button.layer.cornerRadius, 16)
    }

    func testPeriodButtonFixedCornerRadiusDoesNotFollowHeight() {
        let button = StatisticsPeriodButton(frame: CGRect(x: 0, y: 0, width: 80, height: 32))
        button.cornerStyle = .fixed(10)

        button.setNeedsLayout()
        button.layoutIfNeeded()

        XCTAssertEqual(button.layer.cornerRadius, 10)
    }

    func testFormattedSelectedPeriodTimeForToday() {
        let viewModel = StatisticsViewModel()
        let text = viewModel.formattedSelectedPeriodTime(for: "今日")
        let today = formattedToday()
        XCTAssertEqual(text, "\(today)至\(today)")
    }

    func testFormattedSelectedPeriodTimeForThisMonth() {
        let viewModel = StatisticsViewModel()
        let text = viewModel.formattedSelectedPeriodTime(for: "本月")
        XCTAssertTrue(text.contains("至"))
        XCTAssertTrue(text.hasPrefix(firstDayOfMonth()))
    }

    func testUpdateRangeByShortcutAllClearsTimes() async {
        let listJSON = """
        {"code":100000,"msg":"success","data":{"total":0,"data":[]}}
        """.data(using: .utf8)!

        AppStore.shared.session.currentScenicId = 10
        let session = MockURLSession(responses: [listJSON])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()

        await viewModel.updateRangeByShortcut("全部", api: api)

        XCTAssertEqual(viewModel.statisticsRangeText, "全部")
        XCTAssertEqual(viewModel.listStartTime, "")
        XCTAssertEqual(viewModel.listEndTime, "")
        XCTAssertEqual(session.requests.count, 1)
        XCTAssertEqual(session.requests[0].url?.path, "/api/yf-handset-app/photog/analyse/user/daily")
        let queryItems = URLComponents(url: session.requests[0].url!, resolvingAgainstBaseURL: false)?.queryItems ?? []
        XCTAssertEqual(queryItems.first(where: { $0.name == "start_time" })?.value, "")
        XCTAssertEqual(queryItems.first(where: { $0.name == "end_time" })?.value, "")
    }

    func testPhotographerSummaryAndDailyRequests() async throws {
        let summaryJSON = """
        {"code":100000,"msg":"success","data":{"order_amount_sum":"100","order_count":2,"order_price_avg":"50","received_amount_sum":"90","refund_amount_sum":10}}
        """.data(using: .utf8)!
        let listJSON = """
        {"code":100000,"msg":"success","data":{"total":1,"data":[{"date":"2025-12-09","order_count":2,"order_price":"50","refund":"10","received":"90"}]}}
        """.data(using: .utf8)!

        AppStore.shared.session.currentScenicId = 10
        let session = MockURLSession(responses: [summaryJSON, listJSON])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()

        await viewModel.loadStatistics("7日", api: api)
        await viewModel.updateRangeByShortcut("今日", api: api)

        XCTAssertEqual(viewModel.statistics?.orderAmountSum, "100")
        XCTAssertEqual(viewModel.statisticsList.count, 1)
        XCTAssertEqual(session.requests.map { $0.url?.path }, [
            "/api/yf-handset-app/photog/analyse/user",
            "/api/yf-handset-app/photog/analyse/user/daily",
        ])
        XCTAssertTrue(session.requests[0].url?.query?.contains("range=3") == true)
        XCTAssertTrue(session.requests[1].url?.query?.contains("paeg_size=15") == true)
        XCTAssertTrue(session.requests[1].url?.query?.contains("scenic_id=10") == true)
    }

    func testScenicAdminUsesScenicAdminEndpoints() async {
        let summaryJSON = """
        {"code":100000,"msg":"success","data":{"order_amount_sum":"0","order_count":0,"order_price_avg":"0","received_amount_sum":"0","refund_amount_sum":0}}
        """.data(using: .utf8)!
        let listJSON = """
        {"code":100000,"msg":"success","data":{"total":0,"data":[]}}
        """.data(using: .utf8)!

        AppStore.shared.session.roleCode = AppRoleCode.scenicAdmin.rawValue
        AppStore.shared.session.currentScenicId = 20
        let session = MockURLSession(responses: [summaryJSON, listJSON])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()

        await viewModel.loadStatistics("今日", api: api)
        await viewModel.updateRangeByShortcut("今日", api: api)

        XCTAssertEqual(session.requests[0].url?.path, "/api/app/scenic-admin/analyse")
        XCTAssertEqual(session.requests[1].url?.path, "/api/app/scenic-admin/analyse/daily")
    }

    func testStoreAdminUsesStoreEndpoints() async {
        let summaryJSON = """
        {"code":100000,"msg":"success","data":{"order_amount_sum":"0","order_count":0,"order_price_avg":"0","received_amount_sum":"0","refund_amount_sum":0}}
        """.data(using: .utf8)!
        let listJSON = """
        {"code":100000,"msg":"success","data":{"total":0,"data":[]}}
        """.data(using: .utf8)!

        AppStore.shared.session.roleCode = AppRoleCode.storeAdmin.rawValue
        AppStore.shared.session.currentStoreId = 30
        let session = MockURLSession(responses: [summaryJSON, listJSON])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()

        await viewModel.loadStatistics("今日", api: api)
        await viewModel.updateRangeByShortcut("今日", api: api)

        XCTAssertEqual(session.requests[0].url?.path, "/api/app/store/analyse")
        XCTAssertTrue(session.requests[0].url?.query?.contains("store_id=30") == true)
        XCTAssertEqual(session.requests[1].url?.path, "/api/app/store/analyse/daily")
    }

    func testStoreAdminWithoutStoreIdDoesNotRequest() async {
        AppStore.shared.session.roleCode = AppRoleCode.storeAdmin.rawValue
        AppStore.shared.session.currentStoreId = 0
        let session = MockURLSession(responses: [])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()
        var message: String?
        viewModel.onShowMessage = { message = $0 }

        await viewModel.loadStatistics("今日", api: api)

        XCTAssertEqual(message, "请先选择店铺")
        XCTAssertEqual(session.requests.count, 0)
    }

    func testLoadMoreStopsWhenReachedTotal() async {
        let page1JSON = """
        {"code":100000,"msg":"success","data":{"total":1,"data":[{"date":"2025-12-09","order_count":1,"order_price":"10","refund":"0","received":"10"}]}}
        """.data(using: .utf8)!

        AppStore.shared.session.currentScenicId = 10
        let session = MockURLSession(responses: [page1JSON])
        let api = StatisticsAPI(client: APIClient(environment: .testing, session: session))
        let viewModel = StatisticsViewModel()

        await viewModel.updateRangeByShortcut("今日", api: api)
        XCTAssertEqual(viewModel.statisticsList.count, 1)
        XCTAssertFalse(viewModel.canLoadMore)

        await viewModel.loadMore(api: api)
        XCTAssertEqual(viewModel.listCurrentPage, 1)
        XCTAssertEqual(session.requests.count, 1)
    }

    private func formattedToday() -> 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 firstDayOfMonth() -> String {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .gregorian)
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy/MM/dd"
        let calendar = Calendar(identifier: .gregorian)
        let components = calendar.dateComponents([.year, .month], from: Date())
        let firstDay = calendar.date(from: components) ?? Date()
        return formatter.string(from: firstDay)
    }
}
