// Types matching the Go backend store models (internal/store/models.go).
//
// Workload-first refactor: the canonical primitive is a Workload (with
// a Container row per instance). Legacy Project / Stage / Stack /
// StaticSite / Deploy / Instance / Volume types were retired with the
// hard cutover — the WebUI talks to /api/workloads, /api/containers,
// /api/triggers, and friends only. The few "envelope" types still
// useful across the surviving endpoints (ApiEnvelope, BrowseResult,
// FileEntry, registries, settings, DNS, backups, NPM, system stats,
// container stats, etc.) live below.

export interface Registry {
	id: string;
	name: string;
	url: string;
	type: string;
	token: string;
	owner: string;
	has_token?: boolean;
	created_at: string;
	updated_at: string;
}

/** A container image discovered from a registry. */
export interface RegistryImage {
	name: string;
	owner: string;
	full_ref: string;
}

export interface Settings {
	domain: string;
	server_ip: string;
	public_ip: string;
	network: string;
	subdomain_pattern: string;
	notification_url: string;
	npm_url: string;
	npm_email: string;
	/** Returned by GET as a boolean indicating whether the password is set. */
	has_npm_password: boolean;
	/** Sent on PUT to update the password; never returned by GET. */
	npm_password?: string;
	npm_remote: boolean;
	npm_access_list_id: number;
	polling_interval: string;
	base_volume_path: string;
	ssl_certificate_id: number;
	stale_threshold_days: number;
	allowed_volume_paths: string;
	wildcard_dns: boolean;
	dns_provider: string;
	has_cloudflare_api_token: boolean;
	/** Sent on PUT to update the Cloudflare API token; never returned by GET. */
	cloudflare_api_token?: string;
	cloudflare_zone_id: string;
	image_prune_threshold_mb: number;
	proxy_provider: string;
	traefik_entrypoint: string;
	traefik_cert_resolver: string;
	traefik_network: string;
	traefik_api_url: string;
	backup_enabled: boolean;
	backup_interval_hours: number;
	backup_retention_count: number;
	auto_backup_before_deploy: boolean;
	stats_interval_seconds: number;
	stats_retention_hours: number;
	updated_at: string;
}

/** A DNS zone from a provider (e.g., Cloudflare). */
export interface DnsZone {
	id: string;
	name: string;
}

/** A DNS record view for the DNS Records page. */
export interface DnsRecordView {
	fqdn: string;
	type: string;
	content: string;
	consumer_type: string;
	consumer_name: string;
	consumer_id: string;
	status: string;
}

/** A backup metadata record. */
export interface BackupInfo {
	id: string;
	filename: string;
	size_bytes: number;
	backup_type: string;
	created_at: string;
}

/** An SSL certificate from Nginx Proxy Manager. */
export interface NpmCertificate {
	id: number;
	nice_name: string;
	domain_names: string[];
	expires_on: string;
	provider: string;
}

/** Standard API envelope returned by all backend endpoints. */
export interface ApiEnvelope<T> {
	success: boolean;
	data?: T;
	error?: string;
}

/** Response shape for POST /api/discovery/image/inspect */
export interface InspectResult {
	port: number;
	healthcheck: string;
}

/**
 * Item for the EntityPicker command-palette component.
 *
 * `icon` was historically typed as `string` and rendered via `{@html}` —
 * which made it a potential stored-XSS sink the moment a caller built the
 * value from any non-literal data. Narrowed to a controlled token union
 * so every supported glyph is rendered through a known SVG path. Adding a
 * new glyph requires a code change here AND a render-branch in
 * EntityPicker.svelte — keep them in sync.
 */
export type EntityPickerIcon = 'lock' | 'box' | 'folder' | 'branch';

export interface EntityPickerItem {
	value: string;
	label: string;
	description?: string;
	icon?: EntityPickerIcon;
	group?: string;
	disabled?: boolean;
	disabledHint?: string;
}

/** Volume scope determines the sharing level. */
export type VolumeScope =
	| 'instance'
	| 'stage'
	| 'project'
	| 'project_named'
	| 'named'
	| 'ephemeral'
	| 'absolute';

/** Scope metadata returned by GET /api/volumes/scopes. */
export interface VolumeScopeInfo {
	scope: VolumeScope;
	description: string;
	needs_name: boolean;
	path_example: string;
}

/** A file or directory entry in a volume listing. */
export interface FileEntry {
	name: string;
	is_dir: boolean;
	size: number;
	mod_time: string;
}

/** Response from the volume browse endpoint. */
export interface BrowseResult {
	path: string;
	entries: FileEntry[];
}

/** Docker daemon health check result. */
export interface DockerHealth {
	connected: boolean;
	error?: string;
	category?: string;
	hints?: string[];
	platform?: string;
	checked_at?: string;
	latency_ms?: number;
	version?: string;
	api_version?: string;
	os?: string;
	arch?: string;
	kernel?: string;
	operating_system?: string;
	storage_driver?: string;
	root_dir?: string;
	name?: string;
	ncpu?: number;
	memory_total?: number;
	containers?: number;
	running?: number;
	paused?: number;
	stopped?: number;
	images?: number;
}

export interface ProxyHealth {
	connected: boolean;
	provider: string;
	error?: string;
	latency_ms?: number;
	url?: string;
	proxy_hosts?: number;
	/** Routes deployed by Tinyforge itself (instances + static sites). */
	proxy_hosts_managed?: number;
	access_lists?: number;
	certificates?: number;
}

/** A local Docker image. */
export interface LocalImage {
	id: string;
	ref: string;
	tag: string;
	size: number;
	created: number;
}

/** An NPM access list for proxy authentication. */
export interface NpmAccessList {
	id: number;
	name: string;
}

export type ProxyRouteSource = 'instance' | 'static_site';

/** A proxy route managed by a deployed instance or a static site. */
export interface ProxyRoute {
	source: ProxyRouteSource;
	instance_id: string;
	project_id: string;
	project_name: string;
	stage_id: string;
	stage_name: string;
	image_tag: string;
	subdomain: string;
	domain: string;
	container_id: string;
	port: number;
	proxy_route_id: string;
	npm_proxy_id: number;
	status: string;
	created_at: string;
}

/** A persistent event log entry. */
export interface EventLogEntry {
	id: number;
	source: string;
	severity: 'info' | 'warn' | 'error';
	message: string;
	metadata: string;
	created_at: string;
}

/** Severity counts for the event log. */
export interface EventLogStats {
	info: number;
	warn: number;
	error: number;
	total: number;
}

/**
 * A container detected as stale by the backend poller.
 *
 * Shape matches the Go side after the workload refactor:
 * the embedded Container row is the canonical state and the workload/role
 * fields decorate it for display.
 */
export interface StaleContainer {
	container: Container;
	workload_id: string;
	workload_name: string;
	role: string;
	days_stale: number;
}

/** Container CPU, memory, network, and block I/O stats from the Docker stats API. */
export interface ContainerStats {
	timestamp?: string;
	cpu_percent: number;
	memory_usage: number;
	memory_limit: number;
	memory_percent: number;
	network_rx_bytes?: number;
	network_tx_bytes?: number;
	block_read_bytes?: number;
	block_write_bytes?: number;
}

/** One persisted container sample returned by the history endpoints. */
export interface ContainerStatsSample {
	container_id: string;
	owner_type: 'instance' | 'site';
	owner_id: string;
	ts: number;
	cpu_percent: number;
	memory_usage: number;
	memory_limit: number;
	network_rx_bytes: number;
	network_tx_bytes: number;
	block_read_bytes: number;
	block_write_bytes: number;
}

/**
 * A container sample augmented with the human-readable owner name returned
 * by the /system/stats/top endpoint. Container ID is empty for non-admin
 * viewers to avoid leaking workload identifiers across access boundaries.
 */
export interface TopContainerSample extends ContainerStatsSample {
	owner_name: string;
}

/** Host-level snapshot returned by /api/system/stats. */
export interface SystemStats {
	timestamp: string;
	ncpu: number;
	memory_total: number;
	containers: number;
	running: number;
	paused: number;
	stopped: number;
	images: number;
	disk_images_bytes: number;
	disk_containers_bytes: number;
	disk_volumes_bytes: number;
	disk_build_cache_bytes: number;
	disk_images_reclaimable: number;
	disk_containers_reclaimable: number;
	disk_volumes_reclaimable: number;
	disk_build_cache_reclaimable: number;
	disk_total_bytes: number;
}

/** One persisted system sample returned by /api/system/stats/history. */
export interface SystemStatsSample {
	ts: number;
	ncpu: number;
	memory_total: number;
	workload_cpu_percent: number;
	workload_mem_usage: number;
	containers_running: number;
	disk_total_bytes: number;
}

// ── Workload / Container / App ────────────────────────────────────

export type WorkloadKind = 'project' | 'stack' | 'site' | 'plugin' | (string & {});

/**
 * Workload is the unifying primitive after the legacy Project / Stack /
 * StaticSite types were retired. Plugin-native rows whose source/trigger
 * kinds are populated are the only rows the WebUI creates now.
 */
export interface Workload {
	id: string;
	kind: WorkloadKind;
	ref_id: string;
	name: string;
	app_id: string;
	source_kind: string;
	source_config: string;
	trigger_kind: string;
	trigger_config: string;
	public_faces: string;
	parent_workload_id: string;
	notification_url: string;
	webhook_require_signature: boolean;
	// GitOps config-as-code (dockerfile/static sources only). Opt-in: when
	// enabled, the workload's deploy config is declared in `gitops_path`
	// (default ".tinyforge.yml") in its own repo and applied via Sync.
	gitops_enabled: boolean;
	gitops_path: string;
	gitops_last_sync_at: string;
	gitops_commit_sha: string;
	created_at: string;
	updated_at: string;
}

export interface PublicFace {
	subdomain: string;
	domain: string;
	target_service: string;
	target_port: number;
	access_list_id: number;
	enable_ssl: boolean;
}

export interface PluginWorkloadInput {
	name: string;
	group_id?: string;
	parent_workload_id?: string;
	source_kind: string;
	source_config: unknown;
	trigger_kind: string;
	trigger_config: unknown;
	public_faces?: PublicFace[];
	notification_url?: string;
	webhook_require_signature?: boolean;
}

export interface HookKinds {
	sources: string[];
	triggers: string[];
}

/**
 * Canonical container states. The trailing `(string & {})` is the
 * "literal-friendly string" trick — it lets the union accept arbitrary
 * strings (handy when the backend's normalize-state path adds a value that
 * the frontend hasn't caught up with) WITHOUT collapsing to plain `string`,
 * so editor autocomplete and exhaustiveness checks still work on the named
 * literals.
 */
export type ContainerState =
	| 'running'
	| 'stopped'
	| 'failed'
	| 'removing'
	| 'missing'
	| 'starting'
	| 'created'
	| 'restarting'
	| 'paused'
	// eslint-disable-next-line @typescript-eslint/ban-types
	| (string & {});

/** A row from the normalized containers index. */
export interface Container {
	id: string;
	workload_id: string;
	workload_kind: WorkloadKind;
	role: string;
	/** Project containers only; '' for stack and site rows. */
	stage_id: string;
	container_id: string;
	image_ref: string;
	image_tag: string;
	host: string;
	state: ContainerState;
	port: number;
	subdomain: string;
	proxy_route_id: string;
	npm_proxy_id: number;
	last_seen_at: string;
	created_at: string;
	updated_at: string;
}

/**
 * Container row decorated with workload + app names by the backend so the
 * global Containers table can render without N+1 fetches.
 */
export interface ContainerView extends Container {
	workload_name: string;
	app_id?: string;
	app_name?: string;
}

export interface App {
	id: string;
	name: string;
	description: string;
	created_at: string;
	updated_at: string;
}
