<!--
  Static source form — Gitea Pages-alike with optional Deno runtime mode.
  Delegates the git-discovery block (provider/repo/branch/token + folder
  tree) to StaticDiscoveryWizard, then adds the static-only mode radio and
  the render-markdown toggle.

  The parent owns the `StaticFormState` (from `$lib/workload/sourceForms`)
  and binds it here; serialization to `source_config` is done by the parent
  via `staticToConfig` so the shape (incl. preserved storage_* keys) stays
  byte-identical. `StaticFormState extends GitSourceState`, so the same
  object is bound straight into the wizard's `git` slice.
-->
<script lang="ts">
	import type { StaticFormState } from '$lib/workload/sourceForms';
	import type { FolderEntry } from '$lib/api';
	import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
	import StaticDiscoveryWizard from '$lib/components/workload/StaticDiscoveryWizard.svelte';
	import DeployStrategyField from '$lib/components/workload/DeployStrategyField.svelte';
	import { t } from '$lib/i18n';

	interface Props {
		form: StaticFormState;
		/** Flip to the raw-JSON editor (owned by the parent). */
		onAdvanced: () => void;
		/**
		 * Transient discovery status — OPTIONAL pass-through to
		 * StaticDiscoveryWizard. Each defaults to this component's own
		 * internal `$state`, so a parent that doesn't bind them gets the
		 * original "resets on remount" behaviour (this is what the detail/
		 * edit page `apps/[id]` relies on — it binds none of these). The
		 * create wizard `apps/new` binds them up to the PAGE so the loaded
		 * tree + detect/test pills + mode override survive the form
		 * unmounting under the Advanced-JSON / source-kind toggles.
		 */
		modeUserOverride?: boolean;
		treeLoaded?: boolean;
		tree?: FolderEntry[];
		detectStatus?: 'idle' | 'pending' | 'ok' | 'error';
		detectError?: string;
		testStatus?: 'idle' | 'pending' | 'ok' | 'error';
		testError?: string;
	}

	let {
		form = $bindable(),
		onAdvanced,
		// Sentinel: once the user manually toggles the static/deno radio,
		// auto-detection stops overwriting their choice on subsequent tree loads.
		modeUserOverride = $bindable(false),
		// Reflects whether the discovery wizard has loaded a folder tree — gates
		// the "auto-detected Deno" hint exactly like the legacy
		// `staticTree.length > 0` guard did.
		treeLoaded = $bindable(false),
		tree = $bindable([]),
		detectStatus = $bindable('idle'),
		detectError = $bindable(''),
		testStatus = $bindable('idle'),
		testError = $bindable('')
	}: Props = $props();

	function onModeChange() {
		modeUserOverride = true;
	}
</script>

<div class="image-form">
	<div class="image-form-head">
		<span class="editor-title">{$t('apps.new.staticHeader')}</span>
		<button type="button" class="json-escape" onclick={onAdvanced} title={$t('apps.new.switchToJsonTitle')}>
			{$t('apps.new.advancedJson')}
		</button>
	</div>
	<StaticDiscoveryWizard
		bind:git={form}
		variant="static"
		showFolderTree={true}
		bind:folderPath={form.folderPath}
		bind:mode={form.mode}
		bind:modeUserOverride
		bind:treeLoaded
		bind:tree
		bind:detectStatus
		bind:detectError
		bind:testStatus
		bind:testError
		idPrefix="app-static"
	/>
	<fieldset class="static-mode">
		<legend class="sub-label">{$t('apps.new.staticMode')}</legend>
		<label class="radio">
			<input type="radio" name="static-mode" value="static" bind:group={form.mode} onchange={onModeChange} />
			<span>
				<strong>static</strong> {$t('apps.new.staticModeStaticDesc')}
			</span>
		</label>
		<label class="radio">
			<input type="radio" name="static-mode" value="deno" bind:group={form.mode} onchange={onModeChange} />
			<span>
				<strong>deno</strong> {$t('apps.new.staticModeDenoDesc')}
			</span>
		</label>
		{#if !modeUserOverride && form.mode === 'deno' && treeLoaded}
			<p class="hint static-deno-auto">{$t('apps.new.staticDenoAutoDetected')}</p>
		{/if}
	</fieldset>
	<label class="toggle-row">
		<ToggleSwitch bind:checked={form.renderMarkdown} label={$t('apps.new.staticRenderMarkdown')} />
		<span>
			<strong>{$t('apps.new.staticRenderMarkdown')}</strong> {@html $t('apps.new.staticRenderMarkdownDesc')}
		</span>
	</label>
	<label class="toggle-row">
		<ToggleSwitch
			bind:checked={form.reportCommitStatus}
			label={$t('apps.new.sourceReportCommitStatus')}
		/>
		<span>
			<strong>{$t('apps.new.sourceReportCommitStatus')}</strong>
			{@html $t('apps.new.sourceReportCommitStatusDesc')}
		</span>
	</label>
	<DeployStrategyField
		bind:strategy={form.deployStrategy}
		defaultStrategy="recreate"
		denoCaveat={form.mode === 'deno'}
		idPrefix="app-static-strategy"
	/>
	<p class="hint image-form-foot">{$t('apps.new.staticFoot')}</p>
</div>

<style>
	.hint {
		font-size: 0.78rem;
		color: var(--text-tertiary);
		margin: 0;
		line-height: 1.45;
	}
	.sub-label {
		font-family: var(--forge-mono);
		font-size: 0.62rem;
		font-weight: 600;
		letter-spacing: 0.14em;
		text-transform: uppercase;
		color: var(--text-secondary);
	}
	.editor-title {
		margin-left: 0.4rem;
		color: var(--text-secondary);
		font-weight: 600;
		letter-spacing: 0.02em;
	}
	/* ── "Edit as JSON" escape hatch ─────────────────────────
	   Quiet secondary text-link rather than a prominent chip, so it doesn't
	   compete with the form title. */
	.json-escape {
		background: none;
		border: 0;
		padding: 0;
		font-family: var(--forge-mono);
		font-size: 0.62rem;
		font-weight: 600;
		letter-spacing: 0.08em;
		text-transform: uppercase;
		color: var(--text-tertiary);
		cursor: pointer;
		text-decoration: none;
		text-underline-offset: 3px;
		transition: color 120ms ease;
	}
	.json-escape:hover {
		color: var(--forge-accent);
		text-decoration: underline;
	}
	.json-escape:focus-visible {
		outline: 2px solid var(--border-focus);
		outline-offset: 2px;
		border-radius: var(--radius-sm);
	}

	/* ── Image source form shell (shared visual vocabulary) ── */
	.image-form {
		display: flex;
		flex-direction: column;
		gap: 0.9rem;
		border: 1px solid var(--border-primary);
		border-radius: var(--radius-lg);
		background: var(--surface-input);
		padding: 0.85rem 1rem;
	}
	.image-form-head {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		padding-bottom: 0.55rem;
		border-bottom: 1px solid var(--border-primary);
		margin-bottom: 0.2rem;
	}
	.image-form-foot {
		margin-top: 0.2rem;
		padding-top: 0.55rem;
		border-top: 1px dashed var(--border-primary);
	}

	/* ── Static source extras ────────────────────────────── */
	.static-mode {
		display: flex;
		flex-direction: column;
		gap: 0.45rem;
		margin: 0;
		padding: 0.7rem 0.9rem;
		border: 1px solid var(--border-primary);
		border-radius: var(--radius-md);
		background: var(--surface-card);
	}
	.static-mode legend {
		padding: 0 0.3rem;
	}
	.radio {
		display: flex;
		align-items: flex-start;
		gap: 0.55rem;
		padding: 0.35rem 0;
		font-size: 0.88rem;
		color: var(--text-secondary);
		cursor: pointer;
	}
	.radio input {
		margin-top: 0.18rem;
		accent-color: var(--color-brand-500);
	}
	.radio strong,
	.toggle-row strong {
		color: var(--text-primary);
	}
	.toggle-row {
		display: flex;
		align-items: flex-start;
		gap: 0.55rem;
		padding: 0.35rem 0;
		font-size: 0.88rem;
		color: var(--text-secondary);
		cursor: pointer;
	}
	.toggle-row :global(.toggle-switch) {
		margin-top: 0.1rem;
	}
	.static-deno-auto {
		margin-top: 0.35rem;
		padding-top: 0.35rem;
		border-top: 1px dashed var(--border-primary);
		color: var(--color-success-dark);
	}
	:global([data-theme='dark']) .static-deno-auto {
		color: #86efac;
	}
</style>
