// Layer 2 (swarm / torrent / sync) verification and run tasks.
// Kept out of build.gradle so the main build stays readable.
//   ./gradlew :thoughtframe:twoInstanceCheck   (the Layer 2 lock)
//   ./gradlew :thoughtframe:cloneCheck | dedupCheck | v2Check | ...

def swarmLibsDir = layout.buildDirectory.dir('swarm-libs')

// libtorrent4j 2.x expects its native on java.library.path; pull it out of the platform jar.
tasks.register('extractSwarmNative', Copy) {
	group = 'verification'
	description = 'Extract libtorrent4j.so from the platform jar for java.library.path.'
	from({
		configurations.runtimeClasspath.files
				.findAll { it.name.startsWith('libtorrent4j-linux') }
				.collect { zipTree(it) }
	})
	include 'lib/x86_64/*.so'
	eachFile { it.path = it.name }
	includeEmptyDirs = false
	into swarmLibsDir
}

// Headless smoke test: can a libtorrent session start? (backs the torrent:// transport client)
tasks.register('swarmCheck', JavaExec) {
	group = 'verification'
	description = 'Start a libtorrent session to verify the native BitTorrent library loads.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.SwarmCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Two nodes: A seeds a folder, B pulls it over the swarm — watch B's folder fill.
tasks.register('twoNodeCheck', JavaExec) {
	group = 'verification'
	description = 'Seed a folder in node A and pull it into node B over BitTorrent.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.TorrentTwoNodeCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// The full loop: publish a repo to a torrent, seed it, clone torrent://<infohash> from a 2nd node.
tasks.register('cloneCheck', JavaExec) {
	group = 'verification'
	description = 'Publish a repo as a torrent, seed it, and clone it back over torrent://.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.TorrentCloneCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Same, but node A and node B are two separate JVM processes.
tasks.register('twoProcessCheck', JavaExec) {
	group = 'verification'
	description = 'Seed in one JVM and clone in a second JVM over BitTorrent.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.TwoProcessCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Run the torrent publish/seed/pull pages inside the engine's own webapp (thoughtframe/webapp).
tasks.register('torrentWeb', JavaExec) {
	group = 'application'
	description = 'Serve the engine webapp with the torrent pages at /thoughtframe/torrent.html.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.TorrentWebApp'
	workingDir rootProject.projectDir
	standardInput = System.in
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	if (project.hasProperty('demo')) {
		args = ['demo']
	}
}

// Boot the engine webapp, GET /thoughtframe/torrent.html once, and exit (CI-friendly).
tasks.register('torrentWebCheck', JavaExec) {
	group = 'verification'
	description = 'Boot the engine webapp and verify /thoughtframe/torrent.html serves.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.TorrentWebApp'
	workingDir rootProject.projectDir
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	args = ['check']
}

// Drive node A by hand (terminal 1): build + publish + seed a demo repo, print the infohash, hold.
tasks.register('seedNode', JavaExec) {
	group = 'application'
	description = 'Node A: build a repo, publish + seed it, print the infohash, and stay online.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.TorrentSeedMain'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	args = [layout.buildDirectory.dir('nodeA').get().asFile.absolutePath]
}

// Drive node B by hand (terminal 2): clone the infohash node A printed.
// ./gradlew :thoughtframe:pullNode -Pinfohash=<hash>
tasks.register('pullNode', JavaExec) {
	group = 'application'
	description = 'Node B: clone torrent://<infohash> into build/nodeB. Pass -Pinfohash=<hash>.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.TorrentPullMain'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	// Resolve args at execution time: configuration runs on every IDE sync, where
	// -Pinfohash is absent, so project.property('infohash') here would fail the build.
	doFirst {
		if (!project.hasProperty('infohash')) {
			throw new GradleException('Pass -Pinfohash=<hash> (printed by :thoughtframe:seedNode)')
		}
		args = [project.property('infohash'), layout.buildDirectory.dir('nodeB').get().asFile.absolutePath]
	}
}

// Does a second mutable DHT put update the value? (BEP 44 seq question.)
tasks.register('dhtUpdateProbe', JavaExec) {
	group = 'verification'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.DhtUpdateProbe'
	dependsOn 'extractSwarmNative'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Learn the DHT mutable-item API: put(public, secret, entry, salt) / get(public, salt, timeout).
tasks.register('dhtProbe', JavaExec) {
	group = 'verification'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.DhtProbe'
	dependsOn 'extractSwarmNative'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Figure out libtorrent4j Ed25519 arg order.
tasks.register('ed25519Probe', JavaExec) {
	group = 'verification'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.Ed25519Probe'
	dependsOn 'extractSwarmNative'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Launch several peers as separate JVM processes (real peers) for fan-out testing.
tasks.register('peers', JavaExec) {
	group = 'application'
	description = 'Start alpha/beta/gamma/delta as separate processes (9091-9094).'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.sync.ui.PeersLauncher'
	standardInput = System.in
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Launch two Layer-1 nodes (9091/9092) to edit, publish and receive visually.
tasks.register('syncUi', JavaExec) {
	group = 'application'
	description = 'Start two sync nodes at http://127.0.0.1:9091 and :9092 (DHT naming).'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.sync.ui.SyncUiMain'
	standardInput = System.in
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Fully decentralized: two instances resolve each other through the BitTorrent DHT (no shared dir).
tasks.register('dhtTwoInstanceCheck', JavaExec) {
	group = 'verification'
	description = 'Two instances find each other via the DHT (public key = address, BEP 44 head).'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.sync.DhtTwoInstanceCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// LOCK Layer 2: two instances, each a folder, publish + view each other's posts through Sync only.
tasks.register('twoInstanceCheck', JavaExec) {
	group = 'verification'
	description = 'Two instances publish and view each other through the org.thoughtframe.sync facade.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.sync.TwoInstanceCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Prove v2 file-level dedup: pull snapshot 1, then snapshot 2 as a different torrent; measure bytes.
tasks.register('dedupCheck', JavaExec) {
	group = 'verification'
	description = 'Prove v2 file dedup across torrents (loose-object payload, two snapshots).'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.DedupCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Dual addressing: hybrid torrent read as btih + btmh, fetched via a magnet carrying both.
tasks.register('v2Check', JavaExec) {
	group = 'verification'
	description = 'Publish hybrid; address by btih AND btmh; fetch via a dual magnet.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.V2Check'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Probe: does the builder emit v1, v2, or hybrid, and can we read the v2 hash?
tasks.register('v2Probe', JavaExec) {
	group = 'verification'
	description = 'Inspect what TorrentBuilder emits (v1/v2/hybrid).'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.V2Probe'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// The design in miniature: a Git "feed" (posts.jsonl) distributed as immutable delta units over a
// swarm, named by a mutable head; followers pull only the deltas they lack.
tasks.register('feedCheck', JavaExec) {
	group = 'verification'
	description = 'Feed = Git repo of posts; delta units over a swarm; mutable head.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.feed.FeedCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Composite scenario: node A seeds an image + a repo whose index.json carries its magnet; node B
// clones the repo, reads the JSON, and follows the magnet to pull the image.
tasks.register('compositeCheck', JavaExec) {
	group = 'verification'
	description = 'Git-over-BitTorrent index -> magnet -> image, across two JVMs.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.CompositeCheck'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
}

// Pull from the running engine: clone the repo, read index.json, follow the magnet to the image.
// ./gradlew :thoughtframe:pullComposite -Prepoinfohash=<hash>
tasks.register('pullComposite', JavaExec) {
	group = 'application'
	description = 'Clone a repo over the swarm and follow index.json\u0027s magnet to the image.'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.CompositePull'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	args = [project.findProperty('repoinfohash') ?: '',
		layout.buildDirectory.dir('composite').get().asFile.absolutePath]
}

// Isolate the in-engine fetch crash: who fetches (separate session vs the seeder) and on which thread.
tasks.register('pullIsolate', JavaExec) {
	group = 'verification'
	description = 'Reproduce the torrent fetch in isolation. -Pmode=main|thread|same'
	dependsOn 'extractSwarmNative'
	classpath = sourceSets.main.runtimeClasspath
	mainClass = 'org.thoughtframe.git.torrent.libtorrent.PullIsolate'
	jvmArgs "-Djava.library.path=${swarmLibsDir.get().asFile.absolutePath}"
	args = [project.findProperty('mode') ?: 'main']
}
