// The library-provider wire schemas — a browser-safe module (no node imports) so plugin
// CONTRACTS can share these types with their UIs. Mirrors the host's `ProviderEntryInput`
// (crates/punktfunk-host mgmt/library.rs). Identity codecs: plain JSON shapes, so values
// pass through unencoded; the value is the shared type + authoring validation.
import { Schema } from "effect";

export const Artwork = Schema.Struct({
	portrait: Schema.optionalKey(Schema.NullOr(Schema.String)),
	hero: Schema.optionalKey(Schema.NullOr(Schema.String)),
	logo: Schema.optionalKey(Schema.NullOr(Schema.String)),
	header: Schema.optionalKey(Schema.NullOr(Schema.String)),
});
export type Artwork = typeof Artwork.Type;

export const LaunchSpec = Schema.Struct({
	kind: Schema.Literal("command"),
	value: Schema.String,
});
export type LaunchSpec = typeof LaunchSpec.Type;

export const PrepStep = Schema.Struct({
	do: Schema.String,
	undo: Schema.optionalKey(Schema.NullOr(Schema.String)),
});
export type PrepStep = typeof PrepStep.Type;

/**
 * How the host should recognize a title's process once it is running.
 *
 * Every field is optional, and omitting the whole thing is fine: the host tracks the process it
 * spawns for the entry anyway. It matters when your launch command hands off and exits — a launcher
 * client, a `flatpak run`, a front-end that starts an emulator — because then the host has nothing
 * left to watch, and the two behaviors this feeds ("end the session when the game exits" and "end the
 * game when the session ends") go quiet for that title.
 *
 * Send whatever you actually know. `install_dir` is the one worth sending if you send only one: any
 * process running from under it counts as the game.
 */
export const DetectHint = Schema.Struct({
	/** Where the title is installed (absolute path on the host). */
	install_dir: Schema.optionalKey(Schema.NullOr(Schema.String)),
	/** The game's own executable (absolute path on the host). */
	exe: Schema.optionalKey(Schema.NullOr(Schema.String)),
	/** The executable's file name (`Hades.exe`), when its location isn't fixed. Weakest signal. */
	process_name: Schema.optionalKey(Schema.NullOr(Schema.String)),
});
export type DetectHint = typeof DetectHint.Type;

/** Descriptive metadata, flat on the wire beside `title` (mirrors the host's flattened
 * `GameMeta`). All fields optional; values are free-form display strings — the host does not
 * normalize platform/genre vocabularies. */
export const GameMeta = Schema.Struct({
	/** The system the title runs on — `"PS2"`, `"Xbox 360"`, `"SNES"`, … */
	platform: Schema.optionalKey(Schema.NullOr(Schema.String)),
	/** Short blurb for a details pane. */
	description: Schema.optionalKey(Schema.NullOr(Schema.String)),
	developer: Schema.optionalKey(Schema.NullOr(Schema.String)),
	publisher: Schema.optionalKey(Schema.NullOr(Schema.String)),
	/** Year of first release. */
	release_year: Schema.optionalKey(Schema.NullOr(Schema.Number)),
	/** Genre taxonomy from the metadata source (`"RPG"`, `"Platformer"`, …). */
	genres: Schema.optionalKey(Schema.Array(Schema.String)),
	/** Free-form organizational labels (`"co-op"`, `"kids"`, …). */
	tags: Schema.optionalKey(Schema.Array(Schema.String)),
	/** Release region — `"NTSC-U"`, `"PAL"`, `"NTSC-J"`. */
	region: Schema.optionalKey(Schema.NullOr(Schema.String)),
	/** Maximum simultaneous (local) players. */
	players: Schema.optionalKey(Schema.NullOr(Schema.Number)),
});
export type GameMeta = typeof GameMeta.Type;

export const ProviderEntry = Schema.Struct({
	external_id: Schema.String,
	title: Schema.String,
	art: Schema.optionalKey(Artwork),
	launch: Schema.optionalKey(Schema.NullOr(LaunchSpec)),
	prep: Schema.optionalKey(Schema.Array(PrepStep)),
	detect: Schema.optionalKey(DetectHint),
	...GameMeta.fields,
});
export type ProviderEntry = typeof ProviderEntry.Type;
