import XCTest
@testable import HealthProbe

final class PrototypeStoreResetPolicyTests: XCTestCase {
    private var temporaryDirectory: URL!
    private var defaults: UserDefaults!
    private var defaultsSuiteName: String!

    override func setUpWithError() throws {
        temporaryDirectory = FileManager.default.temporaryDirectory
            .appending(path: "HealthProbeResetTests-\(UUID().uuidString)", directoryHint: .isDirectory)
        try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true)
        defaultsSuiteName = "HealthProbeResetTests-\(UUID().uuidString)"
        defaults = UserDefaults(suiteName: defaultsSuiteName)
        defaults.removePersistentDomain(forName: defaultsSuiteName)
    }

    override func tearDownWithError() throws {
        if let temporaryDirectory {
            try? FileManager.default.removeItem(at: temporaryDirectory)
        }
        if let defaults {
            defaults.removePersistentDomain(forName: defaultsSuiteName)
        }
        temporaryDirectory = nil
        defaults = nil
        defaultsSuiteName = nil
    }

    func testApplyIfNeededRemovesPrototypeArchiveAndCacheStoresOnce() throws {
        let files = [
            "HealthProbeRecords.store",
            "HealthProbeRecords.store-wal",
            "HealthProbeArchive.sqlite",
            "HealthProbeArchive.sqlite-shm",
            "HealthProbeCache.sqlite"
        ]
        for file in files {
            try Data("prototype".utf8).write(to: temporaryDirectory.appending(path: file))
        }
        try Data("settings".utf8).write(to: temporaryDirectory.appending(path: "HealthProbeLocal.store"))

        let first = try PrototypeStoreResetPolicy.applyIfNeeded(
            appSupportURL: temporaryDirectory,
            defaults: defaults
        )
        XCTAssertTrue(first.didReset)
        XCTAssertEqual(Set(first.removedURLs.map(\.lastPathComponent)), Set(files))
        for file in files {
            XCTAssertFalse(FileManager.default.fileExists(atPath: temporaryDirectory.appending(path: file).path))
        }
        XCTAssertTrue(FileManager.default.fileExists(atPath: temporaryDirectory.appending(path: "HealthProbeLocal.store").path))

        let second = try PrototypeStoreResetPolicy.applyIfNeeded(
            appSupportURL: temporaryDirectory,
            defaults: defaults
        )
        XCTAssertFalse(second.didReset)
        XCTAssertTrue(second.removedURLs.isEmpty)
    }

    func testRequestResetOnNextLaunchMarksResetPending() throws {
        defaults.set(PrototypeStoreResetPolicy.currentGeneration, forKey: PrototypeStoreResetPolicy.defaultsKey)

        XCTAssertFalse(PrototypeStoreResetPolicy.isResetScheduled(defaults: defaults))

        PrototypeStoreResetPolicy.requestResetOnNextLaunch(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        )

        XCTAssertTrue(PrototypeStoreResetPolicy.isResetScheduled(defaults: defaults))
    }

    func testManualResetDeletesStoresEvenWhenGenerationIsCurrent() throws {
        defaults.set(PrototypeStoreResetPolicy.currentGeneration, forKey: PrototypeStoreResetPolicy.defaultsKey)
        let archiveURL = temporaryDirectory.appending(path: "HealthProbeArchive.sqlite")
        let cacheWALURL = temporaryDirectory.appending(path: "HealthProbeCache.sqlite-wal")
        let archiveJournalURL = temporaryDirectory.appending(path: "HealthProbeArchive.sqlite-journal")
        try Data("archive".utf8).write(to: archiveURL)
        try Data("cache".utf8).write(to: cacheWALURL)
        try Data("journal".utf8).write(to: archiveJournalURL)

        PrototypeStoreResetPolicy.requestResetOnNextLaunch(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        )

        let result = try PrototypeStoreResetPolicy.applyIfNeeded(
            appSupportURL: temporaryDirectory,
            defaults: defaults
        )

        XCTAssertTrue(result.didReset)
        XCTAssertEqual(Set(result.removedURLs.map(\.lastPathComponent)), Set([
            "HealthProbeArchive.sqlite",
            "HealthProbeArchive.sqlite-journal",
            "HealthProbeCache.sqlite-wal"
        ]))
        XCTAssertFalse(FileManager.default.fileExists(atPath: archiveURL.path))
        XCTAssertFalse(FileManager.default.fileExists(atPath: cacheWALURL.path))
        XCTAssertFalse(FileManager.default.fileExists(atPath: archiveJournalURL.path))
        XCTAssertFalse(PrototypeStoreResetPolicy.isResetScheduled(defaults: defaults))
    }

    func testManualResetFlagSurvivesWithoutChangingGenerationKey() throws {
        defaults.set(PrototypeStoreResetPolicy.currentGeneration, forKey: PrototypeStoreResetPolicy.defaultsKey)

        PrototypeStoreResetPolicy.requestResetOnNextLaunch(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        )

        XCTAssertEqual(defaults.integer(forKey: PrototypeStoreResetPolicy.defaultsKey), PrototypeStoreResetPolicy.currentGeneration)
        XCTAssertTrue(defaults.bool(forKey: PrototypeStoreResetPolicy.manualResetDefaultsKey))
    }

    func testRequestResetOnNextLaunchWritesDiskMarker() throws {
        defaults.set(PrototypeStoreResetPolicy.currentGeneration, forKey: PrototypeStoreResetPolicy.defaultsKey)

        PrototypeStoreResetPolicy.requestResetOnNextLaunch(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        )

        XCTAssertTrue(PrototypeStoreResetPolicy.isResetScheduled(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        ))
        XCTAssertTrue(FileManager.default.fileExists(
            atPath: temporaryDirectory
                .appending(path: PrototypeStoreResetPolicy.manualResetMarkerFileName)
                .path
        ))
    }

    func testDiskMarkerTriggersResetWhenDefaultsFlagIsMissing() throws {
        defaults.set(PrototypeStoreResetPolicy.currentGeneration, forKey: PrototypeStoreResetPolicy.defaultsKey)
        defaults.set(false, forKey: PrototypeStoreResetPolicy.manualResetDefaultsKey)
        let archiveURL = temporaryDirectory.appending(path: "HealthProbeArchive.sqlite")
        let markerURL = temporaryDirectory.appending(path: PrototypeStoreResetPolicy.manualResetMarkerFileName)
        try Data("archive".utf8).write(to: archiveURL)
        try Data("pending".utf8).write(to: markerURL)

        let result = try PrototypeStoreResetPolicy.applyIfNeeded(
            appSupportURL: temporaryDirectory,
            defaults: defaults
        )

        XCTAssertTrue(result.didReset)
        XCTAssertEqual(result.removedURLs.map(\.lastPathComponent), ["HealthProbeArchive.sqlite"])
        XCTAssertFalse(FileManager.default.fileExists(atPath: archiveURL.path))
        XCTAssertFalse(FileManager.default.fileExists(atPath: markerURL.path))
        XCTAssertFalse(PrototypeStoreResetPolicy.isResetScheduled(
            defaults: defaults,
            appSupportURL: temporaryDirectory
        ))
    }
}
