<!--
  Dockerfile source form. Shares the provider + repo + branch + token
  git-discovery wiring with the static source (StaticDiscoveryWizard in its
  compact `dockerfile` variant — same handlers, no folder tree). The
  build-step controls (context path, dockerfile path, port) are the only
  dockerfile-specific UI.

  The parent owns the `DockerfileFormState` (from `$lib/workload/sourceForms`)
  and binds it here; serialization to `source_config` is done by the parent
  via `dockerfileToConfig` so the shape (incl. preserved unknown keys + the
  scrubbed static-only keys) stays byte-identical. `DockerfileFormState
  extends GitSourceState`, so the same object is bound into the wizard.
-->
<script lang="ts">
	import type { DockerfileFormState } from '$lib/workload/sourceForms';
	import StaticDiscoveryWizard from '$lib/components/workload/StaticDiscoveryWizard.svelte';
	import ToggleSwitch from '$lib/components/ToggleSwitch.svelte';
	import DeployStrategyField from '$lib/components/workload/DeployStrategyField.svelte';
	import { IconX } from '$lib/components/icons';
	import { t } from '$lib/i18n';

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

	let {
		form = $bindable(),
		onAdvanced,
		detectStatus = $bindable('idle'),
		detectError = $bindable(''),
		testStatus = $bindable('idle'),
		testError = $bindable('')
	}: Props = $props();

	// `touched` flips true on first blur — used by the pill to avoid shouting
	// "required" the instant the user lands on the form.
	let portTouched = $state(false);

	// A cleared <input type="number"> binds to null (not 0) in Svelte 5, and
	// `null <= 0` is false — so a bare `port <= 0` check would pass validation
	// on an empty field. Guard against null/NaN/non-positive here.
	const portValid = $derived(
		typeof form.port === 'number' && Number.isFinite(form.port) && form.port > 0
	);
</script>

<div class="image-form">
	<div class="image-form-head">
		<span class="editor-title">{$t('apps.new.dockerfileHeader')}</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="dockerfile"
		bind:detectStatus
		bind:detectError
		bind:testStatus
		bind:testError
		idPrefix="app-df"
	/>

	<!-- Build-step controls — the only dockerfile-only UI. The form is a
	     two-phase form (locate the code, describe how to build it). A
	     forge-eyebrow divider phrases the conceptual break. -->
	<div class="df-section-break" aria-hidden="false">
		<span class="forge-eyebrow">
			<span class="forge-ember"></span>
			<span class="eb-word">{$t('apps.new.dockerfileBuildEyebrow')}</span>
		</span>
	</div>
	<div class="row">
		<label class="sub" for="app-df-context">
			<span class="sub-label">{$t('apps.new.dockerfileContextPath')}</span>
			<input
				id="app-df-context"
				type="text"
				class="input mono"
				bind:value={form.contextPath}
				placeholder={$t('apps.new.dockerfileContextPathPlaceholder')}
				autocomplete="off"
				spellcheck="false"
			/>
		</label>
		<label class="sub" for="app-df-dockerfile">
			<span class="sub-label">{$t('apps.new.dockerfilePath')}</span>
			<input
				id="app-df-dockerfile"
				type="text"
				class="input mono"
				bind:value={form.dockerfilePath}
				placeholder="Dockerfile"
				autocomplete="off"
				spellcheck="false"
			/>
		</label>
	</div>
	<div class="row">
		<label class="sub" for="app-df-port">
			<span class="sub-label"
				>{$t('apps.new.dockerfilePort')}<span class="req-star" aria-label={$t('apps.new.fieldRequired')}
					>*</span
				></span
			>
			<input
				id="app-df-port"
				type="number"
				min="1"
				max="65535"
				class="input mono"
				bind:value={form.port}
				onblur={() => (portTouched = true)}
				placeholder="8080"
				required
			/>
		</label>
	</div>
	{#if portTouched && !portValid}
		<div class="discover-pill discover-pill-bad">
			<IconX size={12} />
			<span>{$t('apps.new.dockerfilePortRequired')}</span>
		</div>
	{/if}
	<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" idPrefix="app-df-strategy" />
	<p class="hint image-form-foot">{$t('apps.new.dockerfileFoot')}</p>
</div>

<style>
	.input {
		width: 100%;
		background: var(--surface-input);
		border: 1px solid var(--border-input);
		border-radius: var(--radius-lg);
		padding: 0.6rem 0.8rem;
		font-size: 0.92rem;
		color: var(--text-primary);
		font-family: inherit;
		outline: none;
		transition: border-color 120ms ease, box-shadow 120ms ease;
	}
	.input:focus {
		border-color: var(--border-focus);
		box-shadow: 0 0 0 3px var(--accent-soft);
	}
	.input:focus-visible {
		outline: none;
	}
	.hint {
		font-size: 0.78rem;
		color: var(--text-tertiary);
		margin: 0;
		line-height: 1.45;
	}
	/* ── Commit-status toggle row (mirrors the static source form) ── */
	.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 strong {
		color: var(--text-primary);
	}
	.toggle-row :global(.toggle-switch) {
		margin-top: 0.1rem;
	}
	.row {
		display: grid;
		grid-template-columns: 1fr 1fr;
		gap: 0.9rem;
	}
	@media (max-width: 600px) {
		.row {
			grid-template-columns: 1fr;
		}
	}
	.sub {
		display: flex;
		flex-direction: column;
		gap: 0.35rem;
	}
	.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);
	}
	/* Required-field marker — same danger hue as the page-level `.req`
	   badge, rendered as a compact asterisk. */
	.req-star {
		margin-left: 0.2rem;
		color: var(--color-danger);
		font-weight: 700;
	}
	.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);
	}
	/* Conceptual section divider — separates git-discovery from build-step.
	   Same dashed border vocabulary as image-form-foot so it reads as a
	   sibling of the foot hint, not a new pattern. */
	.df-section-break {
		margin-top: 0.45rem;
		padding-top: 0.55rem;
		border-top: 1px dashed var(--border-primary);
	}

	.discover-pill {
		display: inline-flex;
		align-items: center;
		gap: 0.35rem;
		padding: 0.28rem 0.55rem;
		border-radius: var(--radius-md);
		font-family: var(--forge-mono);
		font-size: 0.62rem;
		font-weight: 600;
		letter-spacing: 0.06em;
		line-height: 1;
		align-self: flex-start;
	}
	.discover-pill-bad {
		background: color-mix(in srgb, var(--color-danger) 14%, transparent);
		color: var(--color-danger-dark);
		border: 1px solid color-mix(in srgb, var(--color-danger) 40%, transparent);
	}
	:global([data-theme='dark']) .discover-pill-bad {
		color: #fca5a5;
	}
</style>
