//
//  TravelAlbumAPITests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/29.
//

import XCTest
@testable import suixinkan

@MainActor
/// 旅拍相册 API 测试，覆盖 path、query 与 body。
final class TravelAlbumAPITests: XCTestCase {
    /// 测试列表与创建接口 path。
    func testAlbumListAndCreateUseExpectedPaths() async throws {
        let session = TravelAlbumRecordingURLSession(responses: [Self.listResponse, Self.createResponse])
        let api = TravelAlbumAPI(client: APIClient(session: session))

        let list = try await api.albumList(page: 1, pageSize: 20)
        _ = try await api.createAlbum(
            TravelAlbumCreateRequest(
                name: "2026-06-29-001",
                type: 1,
                orderNumber: nil,
                materialNum: 3,
                materialPrice: 9.9,
                materialPackagePrice: 99,
                photoPrice: 0
            )
        )

        XCTAssertEqual(session.requests.map { $0.url?.path }, [
            "/api/yf-handset-app/photog/travel-album/list",
            "/api/yf-handset-app/photog/travel-album/create"
        ])
        XCTAssertEqual(list.total, 1)
        XCTAssertEqual(list.list.first?.name, "2026-06-29-001")

        let createBody = try bodyObject(from: session.requests[1])
        XCTAssertEqual(createBody["name"] as? String, "2026-06-29-001")
        XCTAssertEqual(createBody["type"] as? Int, 1)
    }

    /// 测试上传素材登记接口 body。
    func testUploadMaterialUsesExpectedBody() async throws {
        let session = TravelAlbumRecordingURLSession(data: Self.uploadMaterialResponse)
        let api = TravelAlbumAPI(client: APIClient(session: session))

        _ = try await api.uploadMaterial(
            TravelAlbumUploadMaterialRequest(
                userEquityTravelId: 8,
                fileName: "DSC_001.JPG",
                fileURL: "https://cdn/a.jpg"
            )
        )

        XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/travel-album/upload-material")
        let body = try bodyObject(from: try XCTUnwrap(session.requests.first))
        XCTAssertEqual(body["user_equity_travel_id"] as? Int, 8)
        XCTAssertEqual(body["file_name"] as? String, "DSC_001.JPG")
        XCTAssertEqual(body["file_url"] as? String, "https://cdn/a.jpg")
    }

    /// 测试素材列表接口携带 order_by 与 is_purchased 参数。
    func testMaterialListUsesOrderByAndPurchasedQuery() async throws {
        let session = TravelAlbumRecordingURLSession(data: Self.materialListResponse)
        let api = TravelAlbumAPI(client: APIClient(session: session))

        _ = try await api.materialList(
            userEquityTravelId: 8,
            page: 2,
            pageSize: 30,
            orderBy: 3,
            isPurchased: 1
        )

        let components = try XCTUnwrap(URLComponents(url: try XCTUnwrap(session.requests.first?.url), resolvingAgainstBaseURL: false))
        let query = Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).map { ($0.name, $0.value) })
        XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/travel-album/material-list")
        XCTAssertEqual(query["user_equity_travel_id"], "8")
        XCTAssertEqual(query["page"], "2")
        XCTAssertEqual(query["page_size"], "30")
        XCTAssertEqual(query["order_by"], "3")
        XCTAssertEqual(query["is_purchased"], "1")
    }

    fileprivate static let listResponse = Data(
        #"{"code":100000,"msg":"success","data":{"total":"1","list":[{"id":"8","store_user_id":"1","name":"2026-06-29-001","type":"1","order_number":"","material_num":"3","material_price":"990","material_package_price":"9900","photo_price":"0","cover_url":"","user_id":"100","status":"1","created_at":"2026","updated_at":"2026","user":{"id":"200","phone":"13800138000"}}]}}"#.utf8
    )

    fileprivate static let createResponse = Data(
        #"{"code":100000,"msg":"success","data":{"id":"9"}}"#.utf8
    )

    fileprivate static let uploadMaterialResponse = Data(
        #"{"code":100000,"msg":"success","data":{"id":"11","user_equity_travel_id":"8","status":"1","order_number":"","user_id":"100","file_name":"DSC_001.JPG","file_type":"2","file_url":"https://cdn/a.jpg","file_size":"2048","cover_url":"","is_purchased":"0","created_at":"2026","updated_at":"2026"}}"#.utf8
    )

    fileprivate static let materialListResponse = Data(
        #"{"code":100000,"msg":"success","data":{"total":"0","list":[]}}"#.utf8
    )
}

private final class TravelAlbumRecordingURLSession: URLSessionProtocol {
    private var responses: [Data]
    private(set) var requests: [URLRequest] = []

    init(data: Data) {
        responses = [data]
    }

    init(responses: [Data]) {
        self.responses = responses
    }

    func data(for request: URLRequest) async throws -> (Data, URLResponse) {
        requests.append(request)
        let data = responses.isEmpty ? TravelAlbumAPITests.listResponse : responses.removeFirst()
        let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
        return (data, response)
    }
}

private func bodyObject(from request: URLRequest) throws -> [String: Any] {
    let data = try XCTUnwrap(request.httpBody)
    return try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any])
}
