import type { AndroidOnboardingProgress, AndroidOnboardingStep } from '../android/types.js';
import type { AndroidEffectDeps } from '../android/flow.js';
import type { IosEffectDeps, IosStepCtx, IosStepView } from '../ios/flow.js';
import type { OnboardingProgress, OnboardingStep } from '../types.js';
import type { NextStepResult, Platform } from './contract.js';
import type { BuildOutputRecord } from '../../output-record.js';
import { androidViewForStep } from '../android/flow.js';
import { brokerBegin, brokerClear, brokerPoll } from './broker-session.js';
/** Facts gathered during preflight; the pure deciders branch only on these. */
export interface PreflightFacts {
    capacitorProject: boolean;
    appId?: string;
    platformsDetected: Platform[];
    authenticated: boolean;
    appRegistered: boolean;
    androidProgress: AndroidOnboardingProgress | null;
    iosProgress: OnboardingProgress | null;
}
/** User input carried into the flow via next_step. */
interface OnboardingInput {
    platform?: string;
    serviceAccountJsonPath?: string;
    runBuild?: boolean;
    checkBuild?: boolean;
    keyId?: string;
    issuerId?: string;
    p8Path?: string;
    serviceAccountMethod?: 'generate' | 'existing';
    playDeveloperId?: string;
    gcpProjectId?: string;
    gcpProjectName?: string;
    androidPackage?: string;
    saMethodChoice?: 'retry' | 'save-anyway' | 'oauth';
    /** Set true at google-sign-in to (re)open the browser for a fresh OAuth — recovery when the browser didn't open, was closed, or the sign-in stalled. */
    reopenSignIn?: boolean;
    /** At google-sign-in: open the broker sign-in link in the user's browser (true) or let them open it (false/omit). */
    openSignInBrowser?: boolean;
    /** The confirmation code the user reads off the broker success page — releases the token on poll. */
    confirmCode?: string;
    /** Answer to the resume prompt: 'continue' resumes the saved step, 'restart' wipes this platform's saved progress and begins again. */
    resumeChoice?: 'continue' | 'restart';
    credentialsExistChoice?: 'backup' | 'cancel';
    keystoreMethod?: 'existing' | 'generate';
    keystorePath?: string;
    keystoreStorePassword?: string;
    keystoreAlias?: string;
    keystoreKeyPassword?: string;
    keystoreNewAlias?: string;
    keystorePasswordMethod?: 'random' | 'manual';
    keystoreCommonName?: string;
    /** Answer to the parked iOS verify-app gate (the TUI Select vocabulary). */
    verifyAction?: 'pick' | 'create-new' | 'autofix' | 'continue' | 'recheck' | 'open' | 'reopen' | 'back' | 'cancel';
    /** The picked App Store app's bundle id — only with verifyAction 'pick'. */
    verifyAppId?: string;
    /**
     * Answer to the parked iOS cert-limit-prompt (S6b): the Apple resource id of
     * the Distribution certificate to revoke, or '__exit__' (the engine's
     * OPTION_CERT_LIMIT_EXIT sentinel) to stop.
     */
    certToRevoke?: string;
    /** Answer to the parked iOS duplicate-profile-prompt (S6b). */
    duplicateProfileAction?: 'delete' | 'exit';
    /**
     * Answer to the parked iOS error recovery screen (S6b): 'retry' re-runs the
     * failing step (carried.retryStep), 'restart' wipes progress + session and
     * starts over, 'exit' stops, 'email-support' surfaces support instructions
     * (MCP-only arm — no host-side opens).
     */
    errorAction?: 'retry' | 'restart' | 'exit' | 'email-support';
    /** Answer to the CI-secrets choice steps (target-select / ask / overwrite / push-confirm / setup / failed). */
    ciSecretAction?: 'github' | 'gitlab' | 'skip' | 'yes' | 'no' | 'replace' | 'retry' | 'continue' | 'confirm' | 'cancel';
    /** Answer to ask-github-actions-setup ('no' maps to the persisted setupMode 'declined'). */
    githubActionsSetup?: 'with-workflow' | 'secrets-only' | 'no';
    /** Answer to ask-export-env (yes/no) and confirm-env-export-overwrite (replace/skip). */
    exportEnvAction?: 'yes' | 'no' | 'replace' | 'skip';
    /** Custom .env target path — only together with exportEnvAction 'yes'. */
    envExportPath?: string;
    /** Answer to pick-package-manager. */
    packageManager?: 'bun' | 'npm' | 'pnpm' | 'yarn';
    /** Answer to pick-build-script: a script name, '__custom__', or '__skip__'. */
    buildScript?: string;
    /** Answer to pick-build-script-custom: the exact custom build command. */
    buildScriptCustom?: string;
    /** Answer to preview-workflow-file: write / view (returns the file text, re-asks) / cancel. */
    workflowFileAction?: 'write' | 'view' | 'cancel';
    /** Answer to the iOS setup-method fork: create fresh credentials via Apple, or import from this Mac's Keychain. */
    setupMethod?: 'create-new' | 'import-existing';
    /** Answer to import-distribution-mode ('__cancel__' switches to the create-new path). */
    importDistribution?: 'app_store' | 'ad_hoc' | '__cancel__';
    /** Answer to import-pick-identity: the chosen identity's SHA-1 (an option value), or '__cancel__' for create-new. */
    identityChoice?: string;
    /** Answer to import-pick-profile: the chosen profile's UUID (an option value), or '__back__' to re-pick the identity. */
    profileChoice?: string;
    /** Answer to the import-no-match-recovery hub. */
    importRecoveryAction?: 'create' | 'provide-profile-path' | 'browser' | 'back';
    /** Answer to import-portal-explanation (the manual Apple-portal walkthrough). */
    portalAction?: 'use-create' | 'open-anyway' | 'use-file' | 'back';
    /** Absolute path to a .mobileprovision file — answers import-provide-profile-path (the MCP's manual-path arm of the TUI's native picker). */
    profilePath?: string;
    /** Answer to import-export-warning: 'go' exports from the Keychain (the one macOS permission dialog), 'back' re-picks the profile, 'exit' stops. */
    exportConfirm?: 'go' | 'back' | 'exit';
}
/** Decide the first/again step for a fresh or resumed session. */
export declare function decideStart(facts: PreflightFacts, progress: OnboardingProgress | null, deps: EngineDeps): Promise<NextStepResult>;
/**
 * Map an interactive IosStepView into a NextStepResult — the iOS mirror of
 * mapAndroidView. State names reuse the engine step ids; option values mirror
 * the TUI Select values. Only NON-SECRET, view-derived data may appear here.
 */
export declare function mapIosView(view: IosStepView, facts: PreflightFacts, ctx?: IosStepCtx): NextStepResult;
export declare function decideIos(facts: PreflightFacts, deps: EngineDeps, opts?: {
    verifyAction?: OnboardingInput['verifyAction'];
    verifyAppId?: string;
    certToRevoke?: string;
    duplicateProfileAction?: 'delete' | 'exit';
    errorAction?: 'retry' | 'restart' | 'exit' | 'email-support';
    /** import-pick-identity answer: an identity SHA-1 or '__cancel__'. */
    identityChoice?: string;
    /** import-pick-profile answer: a profile UUID or '__back__'. */
    profileChoice?: string;
    /** import-no-match-recovery answer. */
    importRecoveryAction?: 'create' | 'provide-profile-path' | 'browser' | 'back';
    /** import-portal-explanation answer. */
    portalAction?: 'use-create' | 'open-anyway' | 'use-file' | 'back';
    /** import-provide-profile-path answer: the .mobileprovision path. */
    profilePath?: string;
    /** import-export-warning answer. */
    exportConfirm?: 'go' | 'back' | 'exit';
    /**
     * S9-S11: the explicit tail step a validated tail answer routed to
     * (drive() → applyMcpTailAnswer). Honored only while the slim tail
     * progress carries credentialsSaved — the same guard as the tail park.
     */
    tailNext?: OnboardingStep;
}): Promise<NextStepResult>;
export declare function mapAndroidView(view: ReturnType<typeof androidViewForStep>, facts: PreflightFacts, opts?: {
    keystorePath?: string;
    keystorePassword?: string;
}): NextStepResult;
export declare function decideAndroid(facts: PreflightFacts, deps: EngineDeps, opts?: {
    signInProceed?: boolean;
    /** Drop any in-flight Google OAuth session and (re)open the browser for a fresh
     *  sign-in — recovery for "still waiting" when the browser never opened / was closed. */
    reopenSignIn?: boolean;
    /** At google-sign-in: open the broker sign-in link in the user's browser (vs. letting them open it). */
    openSignInBrowser?: boolean;
    /** The confirmation code the user reads off the broker success page — released the token on the next poll. */
    confirmCode?: string;
    /**
     * S9-S11: the explicit tail step a validated tail answer routed to
     * (drive() → applyMcpTailAnswer). Honored only while the slim tail
     * progress carries credentialsSaved — the same guard as the tail park.
     */
    tailNext?: AndroidOnboardingStep;
}): Promise<NextStepResult>;
export declare function decideAdvance(facts: PreflightFacts, progress: OnboardingProgress | null, input: OnboardingInput | undefined, deps: EngineDeps): Promise<NextStepResult>;
export interface EngineDeps {
    cwd: string;
    hasSavedKey: () => boolean;
    getAppId: () => Promise<string | undefined>;
    detectPlatforms: () => Promise<Platform[]>;
    isAppRegistered: (appId: string) => Promise<boolean>;
    loadProgress: (appId: string) => Promise<OnboardingProgress | null>;
    registerApp: (appId: string) => Promise<{
        ok: true;
    } | {
        ok: false;
        alreadyExists: boolean;
        error: string;
    }>;
    loadAndroidProgress: (appId: string) => Promise<AndroidOnboardingProgress | null>;
    readBuildRecord: (path: string) => Promise<BuildOutputRecord | null>;
    buildRecordPath: (appId: string, platform: Platform) => string;
    /**
     * Remove a build record (and its QR png) left behind by an earlier build.
     * Called by runBuild BEFORE the hand-off so checkBuild can never read a
     * stale record as the new build's result (hostile-review 2026-06-12).
     * Optional so legacy fixtures keep working; production wires
     * removeBuildOutputRecord.
     */
    clearBuildRecord?: (recordPath: string) => Promise<void>;
    /**
     * The shared iOS flow engine's IO deps (Apple API / CSR / fs / persistence),
     * pre-bound by the driver (buildIosEffectDeps in onboarding-tools.ts for
     * production; canned fakes in tests). decideIos threads the per-app carried
     * session state in on every effect run. Optional so legacy fixtures that
     * never enter the iOS path keep working — a missing helper inside surfaces
     * as a caught effect error, never a crash.
     */
    iosEffectDeps?: IosEffectDeps;
    androidEffectDeps: AndroidEffectDeps;
    /**
     * Optional injectable broker OAuth session for testing. When provided, the engine uses these instead of the
     * disk-persisted broker-session.ts functions. Production omits this and relies on the broker session.
     */
    oauthSession?: {
        begin: typeof brokerBegin;
        poll: typeof brokerPoll;
        clear: typeof brokerClear;
    };
    /**
     * Write the generated/loaded Android keystore (.p12) to a file on disk so the
     * user has a durable copy after onboarding. Called once when the keystore phase
     * completes. Returns the absolute path of the written file.
     *
     * Optional — when omitted the keystore is kept in progress only (no file written).
     * Omitting does not break the flow; the keystore data is always in _keystoreBase64.
     */
    writeKeystoreFile?: (appId: string, base64: string, alias: string) => Promise<string>;
}
export declare function gatherFacts(deps: EngineDeps): Promise<PreflightFacts>;
export declare function runStart(deps: EngineDeps, platform?: Platform): Promise<NextStepResult>;
export declare function runAdvance(deps: EngineDeps, input?: OnboardingInput): Promise<NextStepResult>;
/**
 * Read-only: determine the onboarding state the user is currently on, WITHOUT
 * running any side effect. Mirrors the branch selection of decideStart/
 * decideAndroid/decideIos (preflight → platform → resume step, with the same
 * ask-build → build-ready / .p8-chain → ios-api-key name mapping) but never
 * calls effects.
 */
export declare function resolveCurrentState(facts: PreflightFacts): string;
/** State names the MCP constructs directly that are NOT engine step ids. */
export declare const MCP_ONLY_STATES: readonly string[];
/**
 * Engine step ids (present in the type tables) the MCP can NEVER emit as a
 * state name:
 *  - TUI bootstrap / TUI-only screens: welcome, adding-platform,
 *    the AI build-debug sub-flow (decideIos reroutes its entry to
 *    'build-failed'), the contact-support sub-flow (the MCP's error screen has
 *    the email-support arm instead), the native file pickers (the MCP collects
 *    paths as text), the google-sign-in-running spinner (the MCP parks on
 *    'google-sign-in' via its OAuth session), and view-workflow-diff (the MCP
 *    folds the diff into preview-workflow-file's context — 'view' re-parks);
 *  - the .p8 input chain, collapsed into the single 'ios-api-key' gate;
 *  - 'ask-build', mapped to the shared 'build-ready' choice (decideBuildPhase);
 *  - 'requesting-build', never run over MCP (the C2/D2 handoff + checkBuild
 *    polling replace it).
 */
export declare const MCP_UNREACHABLE_STEPS: ReadonlySet<string>;
/**
 * Read-only "explain the current step" entry point backing the
 * capgo_builder_onboarding_explain tool. Gathers facts (read-only) and returns a
 * plain-language explanation string. Never advances the flow or runs effects.
 */
export declare function explainOnboarding(deps: EngineDeps, input?: {
    state?: string;
}): Promise<string>;
export {};
