//
//  MaterialManagementAPITests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

/// 素材管理 API 路径、参数与请求体测试。
@MainActor
final class MaterialManagementAPITests: XCTestCase {
    func testListBuildsAndroidQueryWithMaterialType() async throws {
        let session = MockURLSession(responses: [envelopeJSON(#"{"total":0,"list":[],"order":null}"#)])
        let api = MaterialManagementAPI(client: APIClient(environment: .testing, session: session))

        _ = try await api.list(status: 4, keyword: "雪山", page: 2, pageSize: 10)

        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.httpMethod, "GET")
        XCTAssertEqual(request.url?.path, "/api/app/media-album/list")
        let query = URLComponents(url: try XCTUnwrap(request.url), resolvingAgainstBaseURL: false)?.queryItems
        XCTAssertEqual(query?.first { $0.name == "status" }?.value, "4")
        XCTAssertEqual(query?.first { $0.name == "keyword" }?.value, "雪山")
        XCTAssertEqual(query?.first { $0.name == "type" }?.value, "1")
        XCTAssertEqual(query?.first { $0.name == "page" }?.value, "2")
        XCTAssertEqual(query?.first { $0.name == "page_size" }?.value, "10")
    }

    func testListDecodesNullableOrderAmounts() async throws {
        let session = MockURLSession(responses: [envelopeJSON(#"{"total":0,"list":[],"order":{"avg_change":-1025,"avg_order_amount":null,"refund_total":null,"total_num":0}}"#)])
        let api = MaterialManagementAPI(client: APIClient(environment: .testing, session: session))

        let response = try await api.list(status: 4, keyword: nil, page: 1, pageSize: 10)

        XCTAssertEqual(response.order?.avgOrderAmount, "0")
        XCTAssertEqual(response.order?.refundTotal, "0")
        XCTAssertEqual(response.order?.avgChange, -1025)
        XCTAssertEqual(response.order?.totalNum, 0)
    }

    func testListDecodesAndroidDefaultsWhenNamesAreMissing() async throws {
        let item = #"{"id":56,"name":"iOS视频素材测试","cover_url":"https://cdn/cover.jpg","created_at":"2026-07-13 11:36:30","status":0,"download_count":0,"likes_count":0,"collect_count":0,"share_count":0,"listing_status":0,"type":"2"}"#
        let session = MockURLSession(responses: [envelopeJSON(#"{"total":1,"list":[\#(item)],"order":{"avg_change":1,"avg_order_amount":null,"refund_total":null,"total_num":0}}"#)])
        let api = MaterialManagementAPI(client: APIClient(environment: .testing, session: session))

        let response = try await api.list(status: 4, keyword: nil, page: 1, pageSize: 10)

        let material = try XCTUnwrap(response.list.first)
        XCTAssertEqual(material.id, 56)
        XCTAssertEqual(material.name, "iOS视频素材测试")
        XCTAssertEqual(material.scenicSpotName, "")
        XCTAssertEqual(material.projectName, "")
    }

    func testDetailOperationDeleteAndTagUseAndroidPaths() async throws {
        let detail = envelopeJSON(#"{"id":8,"status":1,"listing_status":1}"#)
        let empty = envelopeJSON("{}")
        let tag = envelopeJSON("23")
        let session = MockURLSession(responses: [detail, empty, empty, tag])
        let api = MaterialManagementAPI(client: APIClient(environment: .testing, session: session))

        _ = try await api.detail(id: 8)
        try await api.setListingStatus(id: 8, listingStatus: 0)
        try await api.delete(id: 8)
        let tagId = try await api.addTag(name: "snow-tag")

        XCTAssertEqual(tagId, 23)
        XCTAssertEqual(session.requests[0].url?.path, "/api/app/media-album/detail")
        XCTAssertEqual(session.requests[1].url?.path, "/api/app/media-album/operation")
        XCTAssertEqual(session.requests[2].url?.path, "/api/app/media-album/delete")
        XCTAssertEqual(session.requests[3].url?.path, "/api/app/media-album/add-tag")
        XCTAssertTrue(session.requests.prefix(3).allSatisfy { request in
            URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?
                .queryItems?
                .first { $0.name == "type" }?
                .value == "1"
        })
    }

    func testUploadAndEditEncodeAndroidBodyKeys() async throws {
        let session = MockURLSession(responses: [envelopeJSON("{}"), envelopeJSON("{}")])
        let api = MaterialManagementAPI(client: APIClient(environment: .testing, session: session))
        let request = MaterialUploadRequest(
            name: "素材A",
            mediaType: .image,
            mediaList: [
                MaterialUploadMediaItem(
                    originalName: "a.jpg",
                    ossUrl: "https://cdn/a.jpg",
                    size: 1024,
                    fileWidthSize: MaterialUploadFileSize(width: 1920, height: 1080)
                ),
            ],
            coverUrl: "https://cdn/c.jpg",
            coverSize: "512",
            scenicSpotId: 9,
            materialTag: "1,2"
        )

        try await api.upload(request)
        try await api.edit(MaterialUploadRequest(
            id: 7,
            name: "素材B",
            mediaType: .video,
            mediaList: request.mediaList,
            coverUrl: request.coverUrl,
            coverSize: request.coverSize,
            scenicSpotId: request.scenicSpotId,
            materialTag: "3"
        ))

        let uploadBody = try bodyJSON(session.requests[0])
        XCTAssertEqual(session.requests[0].url?.path, "/api/app/media-album/upload")
        XCTAssertEqual(uploadBody["name"] as? String, "素材A")
        XCTAssertEqual(uploadBody["type"] as? String, "1")
        XCTAssertEqual(uploadBody["media_type"] as? String, "1")
        XCTAssertNil(uploadBody["project_id"])
        XCTAssertEqual(uploadBody["material_tag"] as? String, "1,2")
        let media = (uploadBody["media_list"] as? [[String: Any]])?.first
        XCTAssertEqual(media?["original_name"] as? String, "a.jpg")
        XCTAssertEqual(media?["oss_url"] as? String, "https://cdn/a.jpg")

        let editBody = try bodyJSON(session.requests[1])
        XCTAssertEqual(session.requests[1].url?.path, "/api/app/media-album/edit")
        XCTAssertEqual(editBody["id"] as? Int, 7)
        XCTAssertEqual(editBody["type"] as? String, "1")
        XCTAssertEqual(editBody["media_type"] as? String, "2")
        XCTAssertNil(editBody["project_id"])
    }

    func testMaterialOSSObjectKeyMatchesAndroidFolderLayout() {
        var calendar = Calendar(identifier: .gregorian)
        calendar.timeZone = TimeZone(secondsFromGMT: 0)!
        let date = calendar.date(from: DateComponents(year: 2026, month: 7, day: 13))!
        let uuid = UUID(uuidString: "11111111-2222-3333-4444-555555555555")!

        let key = OSSUploadPolicy.objectKey(
            fileName: "video/demo.mp4",
            scenicId: 9,
            moduleType: "material",
            date: date,
            uuid: uuid,
            timeZone: TimeZone(secondsFromGMT: 0)!
        )

        XCTAssertEqual(key, "material/9/0/11111111222233334444555555555555_video_demo.mp4")
    }

    private func envelopeJSON(_ dataJSON: String) -> Data {
        """
        {"code":100000,"msg":"success","data":\(dataJSON)}
        """.data(using: .utf8)!
    }

    private func bodyJSON(_ request: URLRequest) throws -> [String: Any] {
        try XCTUnwrap(JSONSerialization.jsonObject(with: try XCTUnwrap(request.httpBody)) as? [String: Any])
    }
}
