import type { BuildCredentials } from '../../../schemas/build.js';
import type { BuildLogger, BuildRequestOptions, BuildRequestResult } from '../../request.js';
import type { AsyncCommandRunner, CiSecretDiscovery, CiSecretEntry, CiSecretSetupAdvice, CiSecretTarget, CommandRunner } from '../ci-secrets.js';
import type { EnvExportOpts, EnvExportResult } from '../env-export.js';
import type { BuildScriptChoice, GeneratedWorkflow, PackageManager, WorkflowGeneratorOpts } from '../workflow-generator.js';
import type { WorkflowWriteOptions, WorkflowWriteResult } from '../workflow-writer.js';
export type TailStep = 'saving-credentials' | 'detecting-ci-secrets' | 'ci-secrets-setup' | 'ci-secrets-target-select' | 'ask-ci-secrets' | 'checking-ci-secrets' | 'confirm-ci-secret-overwrite' | 'uploading-ci-secrets' | 'ci-secrets-failed' | 'ask-github-actions-setup' | 'confirm-secrets-push' | 'ask-export-env' | 'exporting-env' | 'confirm-env-export-overwrite' | 'overwrite-and-export-env' | 'pick-package-manager' | 'pick-build-script' | 'pick-build-script-custom' | 'preview-workflow-file' | 'view-workflow-diff' | 'writing-workflow-file' | 'ask-build' | 'requesting-build' | 'build-complete';
export type TailStepKind = 'auto' | 'input' | 'choice' | 'done' | 'error';
export interface TailStepOption {
    value: string;
    label?: string;
    note?: string;
}
export interface TailStepView {
    step: string;
    kind: TailStepKind;
    title?: string;
    prompt?: string;
    collect?: string[];
    options?: TailStepOption[];
    message?: string;
}
/**
 * Runtime context for the tail views — the OPTIONAL transient data a driver
 * surfaces from a prior effect. Mirrors the tail subset of `AndroidStepCtx` so
 * the android engine can pass its ctx straight through.
 */
export interface TailStepCtx {
    ciSecretEntries?: CiSecretEntry[];
    ciSecretTargets?: CiSecretTarget[];
    ciSecretSetupAdvice?: CiSecretSetupAdvice[];
    ciSecretRepoLabel?: string | null;
    detectedPackageManager?: string;
    availableScripts?: Record<string, string>;
    recommendedScript?: string | null;
    defaultEnvExportPath?: string;
}
export interface TailEffectProgress {
    appId: string;
    setupMode?: 'undecided' | 'with-workflow' | 'secrets-only' | 'declined';
    ciSecretTarget?: CiSecretTarget | null;
    selectedPackageManager?: PackageManager | null;
    buildScriptChoice?: BuildScriptChoice | null;
    envExportTargetPath?: string;
    /**
     * Android-only marker that gates the random-password backup hint at
     * saving-credentials. DRIVER REQUIREMENT: the driver MUST set this on `progress`
     * when its keystore step auto-generated the store password (the bespoke android
     * tail only held this in React `randomPasswordGenerated` state, never persisted);
     * the engine reads it from `progress` and has no other source. Never set on iOS.
     */
    keystorePasswordGenerated?: boolean;
}
export interface TailEffectDeps<P extends TailEffectProgress = TailEffectProgress> {
    /** Tags the saved-cred store, env-export filename and build/workflow platform. */
    platform: 'ios' | 'android';
    /**
     * Build the platform credential SHAPE written at `saving-credentials` (e.g.
     * ANDROID_KEYSTORE_FILE… on android). Throws on missing inputs — same guards
     * the android engine used inline.
     */
    buildSavedCredentials: (progress: P) => Record<string, string> | Promise<Record<string, string>>;
    /**
     * Lossy fallback used when the driver did not thread the saved-credential map
     * through `carried` (crash-recovery resume). Returns {} when not rebuildable.
     */
    rebuildTailCredentials: (progress: P) => Record<string, string>;
    /**
     * The platform's resume resolver — used by the `saving-credentials` self-heal
     * guard to detect a progress that should resume elsewhere.
     */
    resumeStep: (progress: P) => string;
    updateSavedCredentials: (appId: string, platform: 'ios' | 'android', credentials: Record<string, string>) => Promise<void>;
    loadProgress: (appId: string) => Promise<P | null>;
    /**
     * Persist progress. NOTE: the POST-SAVE tail never calls this — saving-credentials
     * deletes progress.json and every later tail step runs purely from transient/
     * carried (the bespoke android tail is in-memory-only), so persisting would
     * re-create the deleted file. Kept on the surface so drivers can still supply it
     * (and for symmetry with the pre-save engine), but unused by runTailEffect.
     */
    saveProgress: (appId: string, progress: P) => Promise<void>;
    deleteProgress: (appId: string) => Promise<void>;
    createCiSecretEntries?: (credentials: Partial<BuildCredentials>, apiKey?: string) => CiSecretEntry[];
    detectCiSecretTargets?: (runner?: CommandRunner) => CiSecretDiscovery;
    getCiSecretRepoLabelAsync?: (target: CiSecretTarget, runner?: AsyncCommandRunner) => Promise<string | null>;
    listExistingCiSecretKeysAsync?: (target: CiSecretTarget, keys: string[], runner?: AsyncCommandRunner) => Promise<string[]>;
    uploadCiSecretsAsync?: (target: CiSecretTarget, entries: CiSecretEntry[], existingKeys?: string[], runner?: AsyncCommandRunner, onProgress?: (current: number, total: number, keyName: string) => void) => Promise<void>;
    exportCredentialsToEnv?: (opts: EnvExportOpts) => EnvExportResult;
    defaultExportPath?: (appId: string, platform: 'ios' | 'android') => string;
    generateWorkflow?: (opts: WorkflowGeneratorOpts) => GeneratedWorkflow;
    writeWorkflowFile?: (opts: WorkflowGeneratorOpts, writeOptions?: WorkflowWriteOptions) => WorkflowWriteResult;
    requestBuildInternal?: (appId: string, options: BuildRequestOptions, silent?: boolean, logger?: BuildLogger) => Promise<BuildRequestResult>;
    /**
     * The streaming BuildLogger the TUI threads into requestBuildInternal (the 4th
     * arg). On android it streams every line into `setBuildOutput`; the engine just
     * forwards it. When absent, requestBuildInternal is called without a logger.
     */
    logger?: BuildLogger;
    /**
     * The build VIEWER sink — DISTINCT from `onLog` (the side-log). The bespoke
     * android tail (app.tsx ~L1654-1740) writes the build header / blank+queued /
     * ⚠ failure / no-key UX / catch lines via `setBuildOutput` (a dedicated build
     * output pane), NOT via the side-log `addLog`. The shared engine forwards
     * those build-viewer lines here so the driver can route them to the right
     * sink. OPTIONAL — absent on iOS (and legacy callers), where the lines are
     * simply dropped and routing is unaffected.
     */
    onBuildOutput?: (line: string) => void;
    /**
     * Resolves the Capgo API key the build request should use, mirroring the
     * android tail's CLI-flag-over-saved precedence (`apikey ?? findSavedKeySilent()`).
     * Returns undefined when no key is resolvable — in which case requesting-build
     * skips the build attempt and finishes at build-complete (the android no-key UX).
     * When this dep is ABSENT the engine falls back to the legacy empty-string apikey
     * so existing callers/tests that never resolved a key keep working.
     */
    resolveApikey?: () => string | undefined;
    /**
     * Per-key upload progress, forwarded as the 5th arg of uploadCiSecretsAsync.
     * The android tail feeds this into `setCiSecretUploadProgress`. No-op when absent.
     */
    onCiSecretUploadProgress?: (current: number, total: number, keyName: string) => void;
    /**
     * The 2-phase checking-ci-secrets status text ('Resolving GitHub repository…'
     * then 'Checking existing env vars in <repo>…'). The android tail feeds this into
     * `setCiSecretCheckPhase`. This is the ONLY sink for the check phases — they are
     * intentionally NOT surfaced via `onStatus`, which the driver routes to the
     * oauth/gcp panes (sending the check phases there would corrupt those). No-op
     * when absent.
     */
    onCiSecretCheckPhase?: (phase: string) => void;
    /**
     * The ci-secrets-failed reason (repo-null / catch in checking-ci-secrets). The
     * android tail feeds this into `setCiSecretError`, which the CiSecretsFailedStep
     * renders. Also surfaced via `transient.ciSecretError`. OPTIONAL — no-op when
     * absent (the failed-step view falls back to its generic message).
     */
    onCiSecretError?: (message: string) => void;
    /** Reads the project's package.json scripts map. */
    getPackageScripts?: () => Record<string, string>;
    /** Detects the web-framework project type (best-effort; may resolve null). */
    findProjectType?: (options?: {
        quiet?: boolean;
    }) => Promise<string | null>;
    /** Maps a detected project type to its recommended build script name. */
    findBuildCommandForProjectType?: (projectType: string) => Promise<string | null>;
    /**
     * Workflow-file telemetry hook (e.g. 'workflow-file-written'). The android tail
     * calls `trackWorkflowEvent`. No-op when absent.
     */
    trackWorkflowEvent?: (event: string, options?: {
        decision?: string;
    }) => void;
    /**
     * DRIVER-HELD transient tail state, threaded back into each effect. The TUI
     * resolves these ONCE (at `saving-credentials`) and keeps them in React state;
     * a headless driver mirrors that by capturing `TailEffectResult.transient` and
     * passing it back here on the NEXT effect. NEVER persisted to progress.json.
     */
    carried?: {
        savedCredentials?: Record<string, string>;
        ciSecretEntries?: CiSecretEntry[];
        ciSecretExistingKeys?: string[];
        /**
         * Whether the workflow file did NOT exist when previewed (app.tsx's
         * `previewIsNew`, resolved at `preview-workflow-file` via existsSync — driver
         * state, never persisted). `writing-workflow-file` logs '✔ Wrote' vs
         * '✔ Overwrote' from it. Absent/undefined defaults to NEW ('Wrote'), matching
         * the bespoke React `useState(true)` default.
         */
        workflowIsNew?: boolean;
    };
    onStatus?: (message: string) => void;
    onLog?: (message: string, color?: string) => void;
    /** Internal-only diagnostic line → the support internal log (main PR #2406). Optional; no-op when absent. */
    onInternalLog?: (line: string) => void;
    signal?: AbortSignal;
}
export interface TailEffectResult<P extends TailEffectProgress = TailEffectProgress> {
    /** Updated progress after the effect ran (matches what was persisted). */
    progress: P;
    /** Explicit next step (a platform step id — string so each platform widens it). */
    next?: string;
    /** Transient runtime data that lives in the driver but is NOT persisted. */
    transient?: TailTransient;
}
/** The tail subset of a platform's transient ctx. Every field is optional. */
export interface TailTransient {
    ciSecretEntries?: CiSecretEntry[];
    savedCredentials?: Record<string, string>;
    ciSecretTargets?: CiSecretTarget[];
    ciSecretSetupAdvice?: CiSecretSetupAdvice[];
    ciSecretRepoLabel?: string | null;
    ciSecretExistingKeys?: string[];
    ciSecretUploadSummary?: string;
    envExportPath?: string;
    workflowFilePath?: string;
    buildUrl?: string;
    buildOutput?: string[];
    aiJobId?: string;
    /** Workflow-builder script preload (resolved at uploading-ci-secrets, with-workflow). */
    availableScripts?: Record<string, string>;
    recommendedScript?: string | null;
    /** Set when env-export found nothing to write or threw — routed to build-complete, never thrown. */
    envExportError?: string;
    /** Set when requesting-build THREW — routed to build-complete, never thrown (app.tsx ~L1733). */
    error?: string;
    /** Set on the ci-secrets-failed routes (repo-null / catch) so the failed-step view can render the reason. */
    ciSecretError?: string;
}
export type TailInput = {
    step: 'ci-secrets-setup';
    value: 'retry' | 'skip';
} | {
    step: 'ci-secrets-target-select';
    ciSecretTarget: CiSecretTarget | null;
} | {
    step: 'ask-ci-secrets';
    value: 'yes' | 'no';
} | {
    step: 'confirm-ci-secret-overwrite';
    value: 'replace' | 'skip';
} | {
    step: 'ci-secrets-failed';
    value: 'retry' | 'continue';
} | {
    step: 'ask-github-actions-setup';
    value: 'with-workflow' | 'secrets-only' | 'no';
} | {
    step: 'confirm-secrets-push';
    value: 'confirm' | 'cancel';
} | {
    step: 'ask-export-env';
    value: 'no';
} | {
    step: 'ask-export-env';
    value: 'yes';
    envExportTargetPath: string;
} | {
    step: 'confirm-env-export-overwrite';
    value: 'replace' | 'skip';
} | {
    step: 'pick-package-manager';
    selectedPackageManager: PackageManager;
} | {
    step: 'pick-build-script';
    value: '__custom__';
} | {
    step: 'pick-build-script';
    buildScriptChoice: BuildScriptChoice;
} | {
    step: 'pick-build-script-custom';
    command: string;
} | {
    step: 'preview-workflow-file';
    value: 'write' | 'view' | 'cancel';
} | {
    step: 'view-workflow-diff';
    value: 'close';
} | {
    step: 'ask-build';
    value: 'yes' | 'no';
};
/**
 * Pure: a UI-framework-neutral description of a tail step. Mirrors the matching
 * <Select>/prompt the TUI renders. Moved verbatim from `androidViewForStep`'s
 * tail cases — the android engine adapts the returned view to `AndroidStepView`.
 */
export declare function tailViewForStep(step: TailStep, progress: TailEffectProgress | null, ctx: TailStepCtx): TailStepView;
/**
 * Pure state write for each tail choice/input step — returns a NEW progress
 * object (spread — never mutate). Navigation-only / spinner-gate inputs return
 * progress unchanged. Moved verbatim from `applyAndroidInput`'s tail reducers.
 */
export declare function applyTailInput<P extends TailEffectProgress>(step: TailStep, progress: P, input: TailInput): P;
/**
 * Dispatches to the right tail effect handler. Moved verbatim from
 * `runAndroidEffect`'s tail cases. Platform-specific calls are parameterised via
 * `deps.platform` / `deps.buildSavedCredentials` / `deps.rebuildTailCredentials`
 * / `deps.resumeStep`; everything else is unchanged.
 */
export declare function runTailEffect<P extends TailEffectProgress>(step: TailStep, progress: P, deps: TailEffectDeps<P>): Promise<TailEffectResult<P>>;
