import SwiftUI

/// Scene identifier for the single main window.
private let mainWindowId = "main"

/// Native app entry point for iOS, iPadOS, and macOS.
/// Opens `.vnd` invitations via `onOpenURL` and routes them to the receive flow.
@main
struct VniDropApp: App {
	@StateObject private var externalInvitations = ExternalInvitationController()
	#if DIRECT_DISTRIBUTION && os(macOS)
	// Sparkle auto-updater, present only in the direct-download (.dmg) build.
	@StateObject private var updater = SparkleUpdaterController()
	#endif

	var body: some Scene {
		#if os(macOS)
		// A single-instance `Window` (not `WindowGroup`): the app must never open a
		// second window. `Window` also drops the ⌘N "New Window" command.
		Window(Text(verbatim: "VniDrop"), id: mainWindowId) {
			RootView(dependencies: makeAppDependencies(externalInvitations: externalInvitations))
				.ignoresSafeArea()
				.onOpenURL(perform: openInvitation)
		}
		#if DIRECT_DISTRIBUTION
		.commands {
			UpdatesCommands(controller: updater)
		}
		#endif
		#else
		WindowGroup(id: mainWindowId) {
			RootView(dependencies: makeAppDependencies(externalInvitations: externalInvitations))
				.ignoresSafeArea()
				.onOpenURL(perform: openInvitation)
		}
		#endif
	}

	/// Reads a `.vnd` invitation document under a security scope, enforcing the
	/// 64 KiB / strict-UTF-8 rules from `ContentView.swift`.
	private func openInvitation(_ url: URL) {
		guard url.pathExtension.caseInsensitiveCompare(vniDropInvitationExtension) == .orderedSame else {
			externalInvitations.reportOpenFailure(message: "This is not a VniDrop invitation")
			return
		}
		let started = url.startAccessingSecurityScopedResource()
		defer { if started { url.stopAccessingSecurityScopedResource() } }
		do {
			let values = try url.resourceValues(forKeys: [.fileSizeKey])
			if let size = values.fileSize, size > maxVniDropInvitationBytes {
				throw InvitationError.tooLarge
			}
			let data = try Data(contentsOf: url, options: .mappedIfSafe)
			let raw = try decodeInvitationBytes(data)
			externalInvitations.openInvitation(raw: raw)
		} catch {
			externalInvitations.reportOpenFailure(
				message: (error as? LocalizedError)?.errorDescription ?? "The invitation could not be opened"
			)
		}
	}
}
