import Foundation
import Combine

/// Receive-destination descriptor, ported from `core/FileSystemService.kt`.
enum ReceiveFolderKind: String, Codable, Sendable {
	case fileSystemPath
	case iosSecurityScopedUrl
}

struct ReceiveFolder: Equatable, Codable, Sendable {
	let kind: ReceiveFolderKind
	let value: String
	let displayName: String
}

enum FolderAccessStatus {
	case writable
	case permissionRequired
	case unavailable
}

/// Persisted app preferences, ported from `preferences/AppPreferencesRepository.kt`.
/// Backed by `UserDefaults` instead of DataStore; keys and semantics match.
struct AppPreferences: Equatable {
	var username: String
	var receiveFolder: ReceiveFolder
	var themeMode: ThemeMode
	var diagnosticsEnabled: Bool
	var diagnosticsInstallId: String
}

struct AppPreferencesDefaults {
	let username: String
	let receiveFolder: ReceiveFolder
	let themeMode: ThemeMode
	var diagnosticsEnabled: Bool = false
}

@MainActor
final class AppPreferencesRepository: ObservableObject {
	@Published private(set) var preferences: AppPreferences

	private let defaults: UserDefaults
	private let fallback: AppPreferencesDefaults

	private enum Key {
		static let username = "username"
		static let receiveFolderKind = "receive_folder_kind"
		static let receiveFolderValue = "receive_folder_value"
		static let receiveFolderDisplayName = "receive_folder_display_name"
		static let themeMode = "theme_mode"
		static let diagnosticsEnabled = "diagnostics_enabled"
		static let diagnosticsInstallId = "diagnostics_install_id"
	}

	init(defaults: UserDefaults = .standard, fallback: AppPreferencesDefaults) {
		self.defaults = defaults
		self.fallback = fallback
		self.preferences = Self.read(from: defaults, fallback: fallback)
	}

	private static func read(from defaults: UserDefaults, fallback: AppPreferencesDefaults) -> AppPreferences {
		let username = (defaults.string(forKey: Key.username)).flatMap { $0.isEmpty ? nil : $0 } ?? fallback.username
		let folder = resolveReceiveFolder(defaults, fallback: fallback.receiveFolder)
		let themeMode = defaults.string(forKey: Key.themeMode).flatMap(ThemeMode.init(rawValue:)) ?? fallback.themeMode
		let diagnostics = defaults.object(forKey: Key.diagnosticsEnabled) as? Bool ?? fallback.diagnosticsEnabled
		let installId = defaults.string(forKey: Key.diagnosticsInstallId) ?? ""
		return AppPreferences(
			username: username,
			receiveFolder: folder,
			themeMode: themeMode,
			diagnosticsEnabled: diagnostics,
			diagnosticsInstallId: installId
		)
	}

	private static func resolveReceiveFolder(_ defaults: UserDefaults, fallback: ReceiveFolder) -> ReceiveFolder {
		let kind = defaults.string(forKey: Key.receiveFolderKind)
			.flatMap(ReceiveFolderKind.init(rawValue:)) ?? fallback.kind
		let value = defaults.string(forKey: Key.receiveFolderValue).flatMap { $0.isEmpty ? nil : $0 } ?? fallback.value
		let displayName = defaults.string(forKey: Key.receiveFolderDisplayName)
			.flatMap { $0.isEmpty ? nil : $0 } ?? fallback.displayName
		return ReceiveFolder(kind: kind, value: value, displayName: displayName)
	}

	private func reload() {
		preferences = Self.read(from: defaults, fallback: fallback)
	}

	func setUsername(_ username: String) {
		defaults.set(username.trimmingCharacters(in: .whitespacesAndNewlines), forKey: Key.username)
		reload()
	}

	func setReceiveFolder(_ folder: ReceiveFolder) {
		defaults.set(folder.kind.rawValue, forKey: Key.receiveFolderKind)
		defaults.set(folder.value, forKey: Key.receiveFolderValue)
		defaults.set(folder.displayName, forKey: Key.receiveFolderDisplayName)
		reload()
	}

	func resetReceiveFolder() {
		setReceiveFolder(fallback.receiveFolder)
	}

	func setThemeMode(_ mode: ThemeMode) {
		defaults.set(mode.rawValue, forKey: Key.themeMode)
		reload()
	}

	func setDiagnosticsEnabled(_ enabled: Bool) {
		defaults.set(enabled, forKey: Key.diagnosticsEnabled)
		reload()
	}

	@discardableResult
	func ensureDiagnosticsInstallId() -> String {
		let existing = preferences.diagnosticsInstallId
		if !existing.isEmpty { return existing }
		let created = UUID().uuidString
		defaults.set(created, forKey: Key.diagnosticsInstallId)
		reload()
		return created
	}
}
