//
//  UploadTests.swift
//  suixinkanTests
//
//  Created by Codex on 2026/6/22.
//

import XCTest
@testable import suixinkan

@MainActor
/// 上传模块测试，覆盖 OSS 上传策略和 STS 配置接口。
final class UploadTests: XCTestCase {
    /// 测试文件校验能识别空文件、超大文件和不支持扩展名。
    func testOSSUploadPolicyValidatesFileConstraints() {
        XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: 0, fileName: "avatar.jpg")) { error in
            XCTAssertEqual(error as? OSSUploadError, .emptyFile)
        }
        XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: OSSUploadPolicy.maxFileSize + 1, fileName: "avatar.jpg")) { error in
            XCTAssertEqual(error as? OSSUploadError, .fileTooLarge)
        }
        XCTAssertThrowsError(try OSSUploadPolicy.validate(dataSize: 12, fileName: "avatar.txt")) { error in
            XCTAssertEqual(error as? OSSUploadError, .unsupportedFileType)
        }
        XCTAssertNoThrow(try OSSUploadPolicy.validate(dataSize: 12, fileName: "avatar.heic"))
    }

    /// 测试 objectKey 按业务模块生成稳定路径并清洗文件名。
    func testOSSUploadPolicyBuildsObjectKeys() {
        let date = Date(timeIntervalSince1970: 1_767_225_600)
        let uuid = UUID(uuidString: "12345678-1234-1234-1234-1234567890ab")!
        let timeZone = TimeZone(secondsFromGMT: 0)!

        XCTAssertEqual(
            OSSUploadPolicy.objectKey(fileName: "a/b.jpg", scenicId: 9, moduleType: "user_avatar", date: date, uuid: uuid, timeZone: timeZone),
            "avatar/20260101/9/123456781234123412341234567890AB_a_b.jpg"
        )
        XCTAssertEqual(
            OSSUploadPolicy.objectKey(fileName: "id.jpg", scenicId: 9, moduleType: "real_name", date: date, uuid: uuid, timeZone: timeZone),
            "real_name/20260101/9/123456781234123412341234567890AB_id.jpg"
        )
        XCTAssertEqual(
            OSSUploadPolicy.objectKey(fileName: "video.mp4", scenicId: 9, moduleType: "task_upload", date: date, uuid: uuid, timeZone: timeZone),
            "task_upload/20260101/9/123456781234123412341234567890AB_video.mp4"
        )
        XCTAssertEqual(
            OSSUploadPolicy.objectKey(fileName: "bank.jpg", scenicId: 9, moduleType: "bank_card", date: date, uuid: uuid, timeZone: timeZone),
            "bank_card/20260101/9/123456781234123412341234567890AB_bank.jpg"
        )
    }

    /// 测试 MIME 类型推断和 URL 拼接规则。
    func testOSSUploadPolicyContentTypeAndURLJoin() {
        XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.jpg", fileType: 2), "image/jpeg")
        XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.png", fileType: 2), "image/png")
        XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.mp4", fileType: 1), "video/mp4")
        XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.unknown", fileType: 1), "video/mp4")
        XCTAssertEqual(OSSUploadPolicy.contentType(for: "a.unknown", fileType: 2), "image/jpeg")
        XCTAssertEqual(OSSUploadPolicy.joinURL(baseURL: "https://cdn.example.com", objectKey: "a/b.jpg"), "https://cdn.example.com/a/b.jpg")
        XCTAssertEqual(OSSUploadPolicy.joinURL(baseURL: "https://cdn.example.com/", objectKey: "a/b.jpg"), "https://cdn.example.com/a/b.jpg")
    }

    /// 测试 STS token 接口使用正确路径、bucket 参数并兼容宽松字段类型。
    func testUploadAPIRequestsSTSConfig() async throws {
        let response = """
        {
          "code": 100000,
          "msg": "success",
          "data": {
            "base_url": 123,
            "endpoint": "oss-cn-hangzhou.aliyuncs.com",
            "region": "cn-hangzhou",
            "bucket": "vipsky",
            "expire_seconds": "900",
            "credentials": {
              "access_key_id": 456,
              "access_key_secret": "secret",
              "security_token": "token"
            }
          }
        }
        """.data(using: .utf8)!
        let session = UploadURLSession(responses: [response])
        let api = UploadAPI(client: APIClient(session: session))

        let config = try await api.aliyunOSSBucket()

        XCTAssertEqual(config.baseUrl, "123")
        XCTAssertEqual(config.expireSeconds, 900)
        XCTAssertEqual(config.credentials.accessKeyId, "456")
        let request = try XCTUnwrap(session.requests.first)
        XCTAssertEqual(request.httpMethod, "GET")
        XCTAssertEqual(request.url?.path, "/api/app/config/get-sts-token")
        XCTAssertEqual(URLComponents(url: request.url!, resolvingAgainstBaseURL: false)?.queryItems?.first?.value, "vipsky")
    }
}

/// 上传测试用 URLSession 替身，按顺序返回预设响应。
private final class UploadURLSession: URLSessionProtocol {
    private var responses: [Data]
    private(set) var requests: [URLRequest] = []

    /// 初始化顺序响应。
    init(responses: [Data]) {
        self.responses = responses
    }

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