//! v3.17 c2 — dogfood usecase: auth landing screen (logged-out brand + CTAs).
//!
//! Port reference: `insight/tools/maestro/flows/auth/landing.yaml`
//! & `insight/tools/maestro/subflows/launch-fresh.yaml`. Drives a
//! cold-launch path to the logged-out landing screen and asserts the six
//! brand / CTA / footer texts are visible:
//! - "Insight"          (brand title)
//! - "Mobile Client"    (brand subtitle)
//! - "Log in to Insight"        (primary CTA)
//! - "Create a new organization" (secondary CTA)
//! - "Privacy Policy"   (footer link)
//! - "Terms of Use"     (footer link)
//!
//! # Why this dogfood
//!
//! Landing screen is the first surface every unauthenticated user sees
//! — its brand identity and CTA wording are product-spec frozen. Maestro
//! covers it with a tiny yaml that runs launch-fresh (clearState +
//! clearKeychain + dev-bubble env switch) then asserts six static texts.
//! smix must port the same contract end-to-end so any future bundle
//! regression that drops a string trips the dogfood immediately.
//!
//! # Reversibility
//!
//! Pure read — six `assert_visible` calls, zero mutation. launch_fresh
//! terminates the app and re-opens via deep link; if a prior session
//! was authenticated, the helper also performs an in-app logout to
//! reach landing. End state lands on the landing screen, suitable for
//! a follow-up login flow.
//!
//! # Capability gap closed (G10) — v3.27 c1 ✅
//!
//! maestro yaml uses `launchApp: { clearState: true, clearKeychain: true }`
//! which wipes MMKV + Keychain at OS level before the app starts. G10
//! was closed in v3.27 c1 (commit `0676ddf`): `App::launch_fresh(bundle_id,
//! clear_state, clear_keychain, app_path)` plus adapter env-var
//! `SMIX_APP_PATH_<NORMALIZED_BUNDLE>` (e.g.
//! `SMIX_APP_PATH_COM_FOCUSAI_APP_MOBILE=/path/to/app`) now wire the
//! host-side `uninstall + install` data-wipe path (the closest aligned
//! `simctl` semantic, since iOS has no native per-app data wipe API).
//! Without the env var the adapter walks the non-clear path
//! (`terminate + launch`) with a graceful warning — preserves v3.x
//! backward compat. The `launch_fresh` helper below (a local example
//! helper, distinct from the SDK's `App::launch_fresh`) approximates
//! the visual end-state via terminate + deep-link + logout-if-needed
//! and does NOT exercise the new SDK wipe path; for true wipe, route
//! through `Step::LaunchApp { clear_state: true }` with the env var set.
//!
//! Concretely, this means: if a prior test left the app in env=production
//! (not staging-tokyo), the env-switch tap in maestro yaml (step 25
//! `tapOn: id: btn-env-staging`) is currently NOT replayed here — we
//! rely on the sim being provisioned with env=staging-tokyo already
//! (sim-insight default per scripts/dev/sim-up.ts). If the landing
//! brand text fails to appear, env-switch may need to be added; that
//! is itself a G10 cap-gap symptom.

use smix_sdk::{App, ExpectationFailure, SwipeDirection, id, text, text_regex};
use std::time::Duration;

#[tokio::main(flavor = "multi_thread", worker_threads = 4)]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let port: u16 = std::env::var("SMIX_RUNNER_PORT")
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(22087);
    let udid = std::env::var("SMIX_UDID").ok();

    println!("smix dogfood — insight auth landing (v3.17 c2)");
    println!("==============================================");
    println!("runner port: {port}");
    println!("udid:        {:?}", udid);
    println!();

    let mut app = App::connect_to_runner(port).await?;
    if let Some(u) = udid {
        app = app.with_udid(u);
    }

    let bundle_id =
        std::env::var("INSIGHT_BUNDLE_ID").unwrap_or_else(|_| "com.focusai.app.mobile".to_string());

    println!("launch_fresh: starting");
    launch_fresh(&app, &bundle_id).await?;
    println!("launch_fresh: done\n");

    match landing_flow(&app).await {
        Ok(()) => {
            println!("\n✓ auth landing flow passed (1-round dogfood)");
            println!("\n🎉 v3.17 c2 dogfood — auth_landing ✅");
            Ok(())
        }
        Err(e) => {
            println!("\n✗ auth landing flow failed");
            for line in e.to_prompt().lines() {
                println!("    {line}");
            }
            Err("dogfood auth landing FAIL".into())
        }
    }
}

// ---- inline helpers (each example self-contained) -----------------------

/// Approximation of maestro `subflows/launch-fresh.yaml`. G10 closed in
/// v3.27 c1: true clearState/clearKeychain parity now wired via
/// `App::launch_fresh` + `SMIX_APP_PATH_<NORM_BUNDLE>` env var. This
/// example helper still uses the visual approximation (terminate +
/// deep-link + logout-if-needed) and does not exercise the SDK wipe
/// path; set the env var and route through `Step::LaunchApp` to validate
/// the closed path on real-sim. Steps:
/// 1. terminate the app
/// 2. re-open via expo dev-client deep link
/// 3. wait for landing OR home-tab landmark (logged-in detection)
/// 4. if landed on home (btn-open-menu visible) → in-app logout to landing
async fn launch_fresh(app: &App, bundle_id: &str) -> Result<(), ExpectationFailure> {
    println!("  launch-fresh: terminate + openurl + handle logged-in (logout if needed)");
    let _ = app.terminate(bundle_id).await;
    tokio::time::sleep(Duration::from_millis(800)).await;
    app.open_url("exp+focus-ai-app://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8081")
        .await?;

    // wait for either landing screen text OR a home-tab landmark.
    app.wait_for(
        &text_regex("Log in to Insight|Open menu|Device"),
        Duration::from_secs(45),
    )
    .await?;

    // logged-in detection — btn-open-menu is the canonical home-tab id.
    if app.find(&id("btn-open-menu")).await? {
        println!("  already logged in, perform logout to reach landing");

        // open the menu drawer; tolerate one missed tap (RN tab-bar jitter).
        for _ in 0..5 {
            if app
                .wait_for(&text("Dashboard"), Duration::from_millis(500))
                .await
                .is_ok()
            {
                break;
            }
            let _ = app.tap(&text("Open menu")).await;
            tokio::time::sleep(Duration::from_millis(800)).await;
        }

        // scroll the drawer up until "Log out" is in view.
        for _ in 0..10 {
            if app.find(&text("Log out")).await? {
                break;
            }
            app.scroll(&text("Log out"), SwipeDirection::Up).await?;
            tokio::time::sleep(Duration::from_millis(400)).await;
        }

        app.tap(&text("Log out")).await?;
        tokio::time::sleep(Duration::from_millis(1500)).await;
        // confirm dialog handle (topmost_modal_filter 选 dialog 内 Log out button)
        if app.find(&text("Cancel")).await? {
            println!("  logout confirm dialog detected, tap 'Log out' to confirm");
            app.tap(&text("Log out")).await?;
        }
        app.wait_for(&text("Log in to Insight"), Duration::from_secs(30))
            .await?;
    }

    // NOTE: G10 (clearState/clearKeychain) closed in v3.27 c1 — host-side
    // wired via App::launch_fresh + SMIX_APP_PATH_<NORM_BUNDLE> env var
    // (commit 0676ddf). This helper walks the visual approximation
    // (terminate + deep-link + logout-if-needed); MMKV/Keychain residue
    // remains by design. To exercise real wipe, set the env var and
    // route through Step::LaunchApp { clear_state: true }.
    Ok(())
}

// ---- landing flow (port of flows/auth/landing.yaml) ---------------------

async fn landing_flow(app: &App) -> Result<(), ExpectationFailure> {
    println!("step 1: assert brand text 'Insight'");
    app.assert_visible(&text("Insight")).await?;

    println!("step 2: assert brand text 'Mobile Client'");
    app.assert_visible(&text("Mobile Client")).await?;

    println!("step 3: assert CTA 'Log in to Insight'");
    app.assert_visible(&text("Log in to Insight")).await?;

    println!("step 4: assert CTA 'Create a new organization'");
    app.assert_visible(&text("Create a new organization"))
        .await?;

    println!("step 5: assert footer link 'Privacy Policy'");
    app.assert_visible(&text("Privacy Policy")).await?;

    println!("step 6: assert footer link 'Terms of Use'");
    app.assert_visible(&text("Terms of Use")).await?;

    Ok(())
}
