//
//  TravelAlbumOTGStorageTests.swift
//  suixinkanTests
//

import XCTest
@testable import suixinkan

/// 旅拍相册 OTG 本地存储测试。
final class TravelAlbumOTGStorageTests: XCTestCase {
    func testStoragePathContainsAccountScenicStoreAndAlbum() throws {
        let root = try makeTempDirectory()
        defer { try? FileManager.default.removeItem(at: root) }
        let store = makeStore(root: root)

        let originals = try store.originalsDirectory(albumId: 33)
        let previews = try store.previewsDirectory(albumId: 33)

        XCTAssertTrue(originals.path.contains("u1_photog/scenic_11/store_22/album_33/originals"))
        XCTAssertTrue(previews.path.contains("u1_photog/scenic_11/store_22/album_33/previews"))
    }

    func testUniqueOriginalFileURLAvoidsOverwrite() throws {
        let root = try makeTempDirectory()
        defer { try? FileManager.default.removeItem(at: root) }
        let store = makeStore(root: root)

        let first = try store.uniqueOriginalFileURL(filename: "IMG/1.JPG", albumId: 33)
        try Data([1]).write(to: first)
        let second = try store.uniqueOriginalFileURL(filename: "IMG/1.JPG", albumId: 33)

        XCTAssertEqual(first.lastPathComponent, "IMG_1.JPG")
        XCTAssertEqual(second.lastPathComponent, "IMG_1_1.JPG")
    }

    func testLoadNormalizesInterruptedTransferToPending() throws {
        let root = try makeTempDirectory()
        defer { try? FileManager.default.removeItem(at: root) }
        let store = makeStore(root: root)
        let file = try store.uniqueOriginalFileURL(filename: "A.JPG", albumId: 33)
        try Data([1, 2, 3]).write(to: file)
        let record = TravelAlbumOTGPhotoRecord(
            id: "1",
            fileName: "A.JPG",
            localPath: file.path,
            capturedAt: "2026-07-07 10:00:00",
            fileSizeBytes: 3,
            status: .uploading,
            progress: 50,
            albumId: 33,
            userId: "u1"
        )

        store.save([record], albumId: 33)
        let loaded = store.load(albumId: 33)

        XCTAssertEqual(loaded.first?.status, .pending)
        XCTAssertEqual(loaded.first?.progress, 0)
        XCTAssertEqual(loaded.first?.localPath, "originals/A.JPG")
        XCTAssertEqual(store.absoluteURL(for: loaded.first?.localPath ?? "", albumId: 33), file)
    }

    func testRemoveAndClearAlbumDeleteFiles() throws {
        let root = try makeTempDirectory()
        defer { try? FileManager.default.removeItem(at: root) }
        let store = makeStore(root: root)
        let file = try store.uniqueOriginalFileURL(filename: "A.JPG", albumId: 33)
        try Data([1]).write(to: file)
        let record = TravelAlbumOTGPhotoRecord(
            id: "1",
            fileName: "A.JPG",
            localPath: file.path,
            capturedAt: "2026-07-07 10:00:00",
            fileSizeBytes: 1,
            status: .pending,
            albumId: 33,
            userId: "u1"
        )
        store.save([record], albumId: 33)

        store.remove(albumId: 33, photoId: "1")

        XCTAssertFalse(FileManager.default.fileExists(atPath: file.path))
        XCTAssertTrue(store.load(albumId: 33).isEmpty)

        let another = try store.uniqueOriginalFileURL(filename: "B.JPG", albumId: 33)
        try Data([2]).write(to: another)
        store.clearAlbum(albumId: 33)

        XCTAssertFalse(FileManager.default.fileExists(atPath: another.deletingLastPathComponent().deletingLastPathComponent().path))
    }

    func testLegacyRecordGetsStableClientPhotoIdOnLoad() throws {
        let root = try makeTempDirectory()
        defer { try? FileManager.default.removeItem(at: root) }
        let store = makeStore(root: root)
        let legacyJSON = """
        {"id":"legacy","sourceId":"legacy","fileName":"A.JPG","localPath":"",\
        "thumbnailPath":"","capturedAt":"2026-07-16 12:00:00","fileSizeBytes":1,\
        "status":"UPLOADED","progress":100,"albumId":33,"userId":"u1",\
        "remoteUrl":"https://cdn/a.jpg","updatedAt":1}
        """.data(using: .utf8)!
        let legacy = try JSONDecoder().decode(TravelAlbumOTGPhotoRecord.self, from: legacyJSON)
        XCTAssertTrue(legacy.clientPhotoId.isEmpty)
        store.save([legacy], albumId: 33)

        let first = try XCTUnwrap(store.load(albumId: 33).first?.clientPhotoId)
        let second = try XCTUnwrap(store.load(albumId: 33).first?.clientPhotoId)

        XCTAssertNotNil(UUID(uuidString: first))
        XCTAssertEqual(first, first.lowercased())
        XCTAssertEqual(first, second)
    }

    func testServerStatusPolicyPromotesDemotesAndPreservesActiveOrChangedRecords() {
        let matched = makeStatusRecord(id: "matched", clientPhotoId: "server-id", status: .failed)
        let missing = makeStatusRecord(id: "missing", clientPhotoId: "missing-id", status: .uploaded)
        let active = makeStatusRecord(id: "active", clientPhotoId: "active-id", status: .uploaded)
        let changedBaseline = makeStatusRecord(id: "changed", clientPhotoId: "changed-id", status: .uploaded)
        var changed = changedBaseline
        changed.progress = 37

        let result = TravelAlbumOTGServerStatusPolicy.reconcile(
            records: [matched, missing, active, changed],
            baselineRecordsById: [
                "matched": matched,
                "missing": missing,
                "active": active,
                "changed": changedBaseline,
            ],
            activePhotoIds: ["active"],
            serverClientPhotoIds: [" SERVER-ID "],
            now: 9
        )
        let byId = Dictionary(uniqueKeysWithValues: result.map { ($0.id, $0) })

        XCTAssertEqual(byId["matched"]?.status, .uploaded)
        XCTAssertEqual(byId["matched"]?.progress, 100)
        XCTAssertEqual(byId["missing"]?.status, .pending)
        XCTAssertEqual(byId["active"]?.status, .uploaded)
        XCTAssertEqual(byId["changed"]?.status, .uploaded)
    }

    private func makeStatusRecord(
        id: String,
        clientPhotoId: String,
        status: TravelAlbumOTGUploadStatus
    ) -> TravelAlbumOTGPhotoRecord {
        TravelAlbumOTGPhotoRecord(
            id: id,
            clientPhotoId: clientPhotoId,
            fileName: "\(id).JPG",
            localPath: "",
            capturedAt: "2026-07-16 12:00:00",
            fileSizeBytes: 1,
            status: status,
            progress: status == .uploaded ? 100 : 0,
            albumId: 33,
            userId: "u1",
            remoteUrl: "https://cdn/\(id).jpg",
            updatedAt: 1
        )
    }

    private func makeStore(root: URL) -> TravelAlbumOTGPhotoStore {
        TravelAlbumOTGPhotoStore(
            context: TravelAlbumOTGStorageContext(
                accountCachePrefix: "u1_photog",
                userId: "u1",
                scenicId: 11,
                storeId: 22
            ),
            applicationSupportDirectory: root.appendingPathComponent("ApplicationSupport", isDirectory: true),
            cachesDirectory: root.appendingPathComponent("Caches", isDirectory: true)
        )
    }

    private func makeTempDirectory() throws -> URL {
        let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
        try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
        return url
    }
}
