//
//  TaskAPITests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/23.
//

import XCTest
@testable import suixinkan

@MainActor
/// 任务 API 测试，覆盖请求参数、请求体和模型解码。
final class TaskAPITests: XCTestCase {
    /// 测试任务列表接口使用正确 path 和 query。
    func testTaskListUsesExpectedPathAndQuery() async throws {
        let session = TaskRecordingURLSession(data: try TestFixture.data(named: "task_list_success"))
        let api = TaskAPI(client: APIClient(session: session))

        _ = try await api.taskList(
            scenicId: 88,
            page: 0,
            pageSize: 0,
            taskStatus: -2,
            taskName: " 精修 ",
            startTime: "2026-06-01",
            endTime: "2026-06-30"
        )

        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/task/list")
        let query = taskQueryItems(from: request)
        XCTAssertEqual(query["scenic_id"], "88")
        XCTAssertEqual(query["page"], "1")
        XCTAssertEqual(query["page_size"], "1")
        XCTAssertEqual(query["task_status"], "0")
        XCTAssertEqual(query["task_name"], "精修")
        XCTAssertEqual(query["start_time"], "2026-06-01")
        XCTAssertEqual(query["end_time"], "2026-06-30")
    }

    /// 测试任务详情接口使用正确 path 和 id。
    func testTaskDetailUsesExpectedPath() async throws {
        let session = TaskRecordingURLSession(data: try TestFixture.data(named: "task_detail_success"))
        let api = TaskAPI(client: APIClient(session: session))

        let detail = try await api.taskDetail(id: 101)

        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/task/info")
        XCTAssertEqual(taskQueryItems(from: request)["id"], "101")
        XCTAssertEqual(detail.urgentHour, 24)
        XCTAssertEqual(detail.taskResult.first?.isVideo, true)
    }

    /// 测试可关联订单接口使用正确景区参数。
    func testAvailableOrderUsesScenicId() async throws {
        let session = TaskRecordingURLSession(data: try TestFixture.data(named: "available_order_success"))
        let api = TaskAPI(client: APIClient(session: session))

        let orders = try await api.availableOrderList(scenicId: 88)

        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/task/available-order")
        XCTAssertEqual(taskQueryItems(from: request)["scenic_id"], "88")
        XCTAssertEqual(orders.first?.orderNumber, "ORD-TASK-001")
    }

    /// 测试发布任务接口提交正确 body。
    func testAddTaskUsesExpectedBody() async throws {
        let session = TaskRecordingURLSession(data: try TestFixture.data(named: "empty_success"))
        let api = TaskAPI(client: APIClient(session: session))
        let request = AddTaskRequest(
            scenicId: "88",
            name: "任务",
            orderNumber: "ORD001",
            remark: "备注",
            urgentHour: 12,
            cloudFile: [CloudFileItem(fileId: 1, remark: "")],
            uploadFile: [UploadFileItem(fileUrl: "https://cdn.example.com/a.jpg", fileName: "a.jpg", remark: "")]
        )

        try await api.addTask(request)

        let urlRequest = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(urlRequest.url?.path, "/api/yf-handset-app/photog/task/create")
        let body = try XCTUnwrap(urlRequest.httpBody)
        let object = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any])
        XCTAssertEqual(object["scenic_id"] as? String, "88")
        XCTAssertEqual(object["name"] as? String, "任务")
        XCTAssertEqual(object["order_number"] as? String, "ORD001")
        XCTAssertEqual(object["photog_remark"] as? String, "备注")
        XCTAssertEqual(object["urgent_hour"] as? Int, 12)
        XCTAssertEqual((object["cloud_file"] as? [[String: Any]])?.first?["file_id"] as? Int, 1)
        XCTAssertEqual((object["upload_file"] as? [[String: Any]])?.first?["file_url"] as? String, "https://cdn.example.com/a.jpg")
    }

    /// 测试云盘文件列表接口使用正确 path 和 query。
    func testCloudFileListUsesExpectedQuery() async throws {
        let session = TaskRecordingURLSession(data: try TestFixture.data(named: "cloud_file_list_success"))
        let api = TaskAPI(client: APIClient(session: session))

        let payload = try await api.cloudFileList(parentFolderId: 501, name: "photo", type: 2, orderBy: 2, page: 0, pageSize: 0)

        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/cloud-driver/list")
        let query = taskQueryItems(from: request)
        XCTAssertEqual(query["parent_folder_id"], "501")
        XCTAssertEqual(query["name"], "photo")
        XCTAssertEqual(query["type"], "2")
        XCTAssertEqual(query["page"], "1")
        XCTAssertEqual(query["page_size"], "1")
        XCTAssertEqual(payload.list.first?.isFolder, true)
    }
}

/// 任务 API 测试用 URLSession，记录请求并返回固定数据。
private final class TaskRecordingURLSession: URLSessionProtocol {
    let data: Data
    private(set) var requests: [URLRequest] = []

    /// 初始化测试 Session。
    init(data: Data) {
        self.data = data
    }

    /// 记录请求并返回成功响应。
    func data(for request: URLRequest) async throws -> (Data, URLResponse) {
        requests.append(request)
        let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)!
        return (data, response)
    }
}

/// 从请求中提取 query 字典。
private func taskQueryItems(from request: URLRequest) -> [String: String] {
    guard
        let url = request.url,
        let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
    else {
        return [:]
    }
    return Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).compactMap { item in
        item.value.map { (item.name, $0) }
    })
}
