import XCTest
@testable import VniDrop

/// Ports settings assertions from `feature/ViewModelsTest.kt` — username debounce
/// persistence and the Storage "delete all transfers" flow.
@MainActor
final class SettingsModelTests: XCTestCase {

	private func makeModel(_ core: FakeCoreGateway, preferences: AppPreferencesRepository) -> SettingsModel {
		SettingsModel(
			environment: PlatformEnvironment(name: "Test", appVersion: "0.1.0", defaultCoreDataDir: NSTemporaryDirectory()),
			deviceInfoProvider: FakeDeviceInfoProvider(),
			fileSystemService: FakeFileSystemService(),
			repository: core,
			preferences: preferences,
			notifications: LocalNotificationService(),
			messages: UiMessageController(),
			bugReports: NoopBugReportService()
		)
	}

	func testUsernameChangeDebouncesAndPersists() async {
		let prefs = Fixtures.preferences(username: "Original")
		let model = makeModel(FakeCoreGateway(), preferences: prefs)

		model.setUsername("Alice")
		XCTAssertEqual(model.state.username, "Alice") // immediate local echo
		await waitUntil { prefs.preferences.username == "Alice" } // persisted after debounce
		XCTAssertEqual(prefs.preferences.username, "Alice")
	}

	func testDeleteAllTransfersDeletesEveryTransfer() async {
		let core = FakeCoreGateway()
		let model = makeModel(core, preferences: Fixtures.preferences())
		core.setState(CoreState(isInitialized: true, transfers: [
			Fixtures.transfer(id: 2, direction: .send, status: .sharing),
			Fixtures.transfer(id: 3, direction: .receive, status: .done),
		]))

		model.deleteAllTransfers()
		await waitUntil { core.deletedTransfers.count == 2 }
		XCTAssertEqual(Set(core.deletedTransfers), [2, 3])
	}

	func testNetworkSettingsExposeCurrentEndpointId() {
		let core = FakeCoreGateway()
		let model = makeModel(core, preferences: Fixtures.preferences())

		core.setState(CoreState(
			isInitialized: true,
			status: CoreStatus(endpointId: "endpoint-for-allowlist", activeTransfers: 0, activeShares: 0)
		))

		XCTAssertEqual(model.state.endpointId, "endpoint-for-allowlist")
	}

	func testApplyCustomRelayRestartsCoreThenPersistsConfiguration() async {
		let core = FakeCoreGateway()
		let preferences = Fixtures.preferences()
		let model = makeModel(core, preferences: preferences)
		model.setRelayMode(.strictCustom)
		model.setRelayURL("  https://relay.example/  ", at: 0)

		model.applyRelayConfiguration()

		await waitUntil { preferences.preferences.relayConfiguration.mode == .strictCustom }
		let expected = RelayConfiguration(mode: .strictCustom, relayURLs: ["https://relay.example"])
		XCTAssertEqual(preferences.preferences.relayConfiguration, expected)
		XCTAssertEqual(core.initializedNetworkConfigurations, [expected])
		XCTAssertFalse(model.state.relayConfigurationIsDirty)
	}

	func testApplyingAutomaticRetainsLastCustomRelayURLs() async {
		let core = FakeCoreGateway()
		let preferences = Fixtures.preferences()
		let relayURLs = ["https://relay.example", "https://backup.example"]
		preferences.setRelayConfiguration(RelayConfiguration(mode: .strictCustom, relayURLs: relayURLs))
		let model = makeModel(core, preferences: preferences)

		model.setRelayMode(.automatic)
		model.applyRelayConfiguration()

		await waitUntil { preferences.preferences.relayConfiguration.mode == .automatic }
		XCTAssertEqual(preferences.preferences.relayConfiguration.relayURLs, relayURLs)
		XCTAssertEqual(core.initializedNetworkConfigurations, [
			RelayConfiguration(mode: .automatic, relayURLs: relayURLs),
		])
		model.setRelayMode(.strictCustom)
		XCTAssertEqual(model.state.relayURLs, relayURLs)
	}

	func testApplyRelayIsBlockedWhileShareIsActive() async {
		let core = FakeCoreGateway()
		let preferences = Fixtures.preferences()
		let model = makeModel(core, preferences: preferences)
		core.setState(CoreState(
			isInitialized: true,
			status: CoreStatus(endpointId: "endpoint", activeTransfers: 0, activeShares: 1)
		))
		model.setRelayMode(.strictCustom)
		model.setRelayURL("https://relay.example", at: 0)

		model.applyRelayConfiguration()
		await Task.yield()

		XCTAssertTrue(core.initializedNetworkConfigurations.isEmpty)
		XCTAssertEqual(preferences.preferences.relayConfiguration, .automatic)
		XCTAssertEqual(model.state.relayApplyErrorKey, "relay_apply_active_transfers")
	}

	func testRepositoryActiveWorkRejectionDoesNotAttemptRollback() async {
		let core = FakeCoreGateway()
		core.initializeResult = .failure(CoreNetworkLifecycleError.activeNetworkWork)
		let preferences = Fixtures.preferences()
		let model = makeModel(core, preferences: preferences)
		let attempted = RelayConfiguration(mode: .strictCustom, relayURLs: ["https://relay.example"])
		model.setRelayMode(.strictCustom)
		model.setRelayURL(attempted.relayURLs[0], at: 0)

		model.applyRelayConfiguration()
		await waitUntil {
			core.initializedNetworkConfigurations.count == 1 && !model.state.isApplyingRelayConfiguration
		}

		XCTAssertEqual(core.initializedNetworkConfigurations, [attempted])
		XCTAssertEqual(preferences.preferences.relayConfiguration, .automatic)
		XCTAssertTrue(model.state.hasActiveNetworkWork)
		XCTAssertEqual(model.state.relayApplyErrorKey, "relay_apply_active_transfers")
	}

	func testFailedRelayApplyRollsBackWithoutPersisting() async {
		let core = FakeCoreGateway()
		core.initializeResults = [.failure(TestError.unimplemented), .success(())]
		let preferences = Fixtures.preferences()
		let model = makeModel(core, preferences: preferences)
		let attempted = RelayConfiguration(mode: .strictCustom, relayURLs: ["https://relay.example"])
		model.setRelayMode(.strictCustom)
		model.setRelayURL(attempted.relayURLs[0], at: 0)

		model.applyRelayConfiguration()
		await waitUntil { core.initializedNetworkConfigurations.count == 2 }

		XCTAssertEqual(core.initializedNetworkConfigurations, [attempted, .automatic])
		XCTAssertEqual(preferences.preferences.relayConfiguration, .automatic)
		XCTAssertEqual(model.state.relayApplyErrorKey, "relay_apply_failed")
	}
}
