//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import XCTest
@testable import SignalServiceKit

class DatabaseCorruptionStateTest: XCTestCase {
    func testCorruptionChanges() throws {
        let defaults = TestUtils.userDefaults()
        func fetch() -> DatabaseCorruptionState {
            DatabaseCorruptionState(userDefaults: defaults)
        }
        func expected(
            _ status: DatabaseCorruptionState.DatabaseCorruptionStatus,
            count: UInt
        ) -> DatabaseCorruptionState {
            DatabaseCorruptionState(status: status, count: count)
        }

        // Initial state
        XCTAssertEqual(fetch(), expected(.notCorrupted, count: 0))

        // After flagging as read corrupted
        DatabaseCorruptionState.flagDatabaseAsReadCorrupted(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.readCorrupted, count: 0))

        // After flagging as corrupted
        DatabaseCorruptionState.flagDatabaseAsCorrupted(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.corrupted, count: 1))

        // After partial recovery
        DatabaseCorruptionState.flagCorruptedDatabaseAsDumpedAndRestored(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.corruptedButAlreadyDumpedAndRestored, count: 1))

        // After full recovery
        DatabaseCorruptionState.flagDatabaseAsRecoveredFromCorruption(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.notCorrupted, count: 1))

        // After another corruption
        DatabaseCorruptionState.flagDatabaseAsCorrupted(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.corrupted, count: 2))

        // Read corruption shouldn't change state after a corruption
        DatabaseCorruptionState.flagDatabaseAsReadCorrupted(userDefaults: defaults)
        XCTAssertEqual(fetch(), expected(.corrupted, count: 2))
    }

    func testLegacyFalseValueWithoutCount() throws {
        let defaults = TestUtils.userDefaults()
        defaults.set(false, forKey: DatabaseCorruptionState.databaseCorruptionStatusKey)

        let expected = DatabaseCorruptionState(status: .notCorrupted, count: 0)
        let actual = DatabaseCorruptionState(userDefaults: defaults)
        XCTAssertEqual(actual, expected)
    }

    func testLegacyTrueValueWithoutCount() throws {
        let defaults = TestUtils.userDefaults()
        defaults.set(true, forKey: DatabaseCorruptionState.databaseCorruptionStatusKey)

        let expected = DatabaseCorruptionState(status: .corrupted, count: 1)
        let actual = DatabaseCorruptionState(userDefaults: defaults)
        XCTAssertEqual(actual, expected)
    }

    func testInvalidData() throws {
        let defaults = TestUtils.userDefaults()
        defaults.set("garbage", forKey: DatabaseCorruptionState.databaseCorruptionStatusKey)
        defaults.set("garbage", forKey: DatabaseCorruptionState.databaseCorruptionCountKey)

        let expected = DatabaseCorruptionState(status: .notCorrupted, count: 0)
        let actual = DatabaseCorruptionState(userDefaults: defaults)
        XCTAssertEqual(actual, expected)
    }
}
