#![recursion_limit = "256"]

use clap::Parser;
use dialoguer::{Confirm, Input};
use serde::Serialize;
use strsim::jaro_winkler;
use tracing_subscriber::Layer;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

use lore::Config;
use lore::cli::autocorrect::{self, CorrectionResult};
use lore::cli::commands::{
    IngestDisplay, InitInputs, InitOptions, InitResult, ListFilters, MrListFilters,
    NoteListFilters, RefreshOptions, RefreshResult, SearchCliFilters, SyncOptions, TimelineParams,
    delete_orphan_projects, handle_explain, open_issue_in_browser, open_mr_in_browser,
    parse_trace_path, print_count, print_count_json, print_cron_install, print_cron_install_json,
    print_cron_status, print_cron_status_json, print_cron_uninstall, print_cron_uninstall_json,
    print_doctor_results, print_drift_human, print_drift_json, print_dry_run_preview,
    print_dry_run_preview_json, print_embed, print_embed_json, print_event_count,
    print_event_count_json, print_file_history, print_file_history_json, print_generate_docs,
    print_generate_docs_json, print_ingest_summary, print_ingest_summary_json, print_list_issues,
    print_list_issues_json, print_list_mrs, print_list_mrs_json, print_list_notes,
    print_list_notes_json, print_related_human, print_related_json, print_search_results,
    print_search_results_json, print_show_issue, print_show_issue_json, print_show_mr,
    print_show_mr_json, print_stats, print_stats_json, print_sync, print_sync_json,
    print_sync_status, print_sync_status_json, print_timeline, print_timeline_json_with_meta,
    print_trace, print_trace_json, print_who_human, print_who_json, query_notes, run_auth_test,
    run_count, run_count_events, run_cron_install, run_cron_status, run_cron_uninstall, run_doctor,
    run_drift, run_embed, run_file_history, run_generate_docs, run_ingest, run_ingest_dry_run,
    run_init, run_init_refresh, run_list_issues, run_list_mrs, run_me, run_related, run_search,
    run_show_issue, run_show_mr, run_stats, run_sync, run_sync_status, run_timeline, run_token_set,
    run_token_show, run_who,
};
use lore::cli::render::{ColorMode, GlyphMode, Icons, LoreRenderer, Theme};
use lore::cli::robot::{RobotMeta, strip_schemas};
use lore::cli::{
    Cli, Commands, CountArgs, CronAction, CronArgs, EmbedArgs, FileHistoryArgs, GenerateDocsArgs,
    IngestArgs, IssuesArgs, MeArgs, MrsArgs, NotesArgs, SearchArgs, StatsArgs, SyncArgs,
    TimelineArgs, TokenAction, TokenArgs, TraceArgs, WhoArgs,
};
use lore::core::db::{
    LATEST_SCHEMA_VERSION, create_connection, get_schema_version, run_migrations,
};
use lore::core::error::{LoreError, RobotErrorOutput};
use lore::core::logging;
use lore::core::metrics::MetricsLayer;
use lore::core::path_resolver::{build_path_query, normalize_repo_path};
use lore::core::paths::{get_config_path, get_db_path, get_log_dir};
use lore::core::project::resolve_project;
use lore::core::shutdown::{ShutdownSignal, install_ctrl_c_handler};
use lore::core::trace::run_trace;
use lore::ingestion::storage::queue::release_all_locked_jobs;
use lore::ingestion::storage::sync_run::SyncRunRecorder;

fn main() {
    let rt = match asupersync::runtime::RuntimeBuilder::new().build() {
        Ok(rt) => rt,
        Err(e) => {
            eprintln!("Failed to build async runtime: {e}");
            std::process::exit(1);
        }
    };

    let rt_handle = rt.handle();

    rt.block_on(async {
    #[cfg(unix)]
    unsafe {
        libc::signal(libc::SIGPIPE, libc::SIG_DFL);
    }

    // Phase 1: Early robot mode detection for structured clap errors
    let robot_mode_early = Cli::detect_robot_mode_from_env();

    // Phase 1.5: Pre-clap arg correction for agent typo tolerance
    let raw_args: Vec<String> = std::env::args().collect();
    let correction_result = autocorrect::correct_args(raw_args, robot_mode_early);

    // Emit correction warnings to stderr (before clap parsing, so they appear
    // even if clap still fails on something else)
    if !correction_result.corrections.is_empty() {
        emit_correction_warnings(&correction_result, robot_mode_early);
    }

    let cli = match Cli::try_parse_from(&correction_result.args) {
        Ok(cli) => cli,
        Err(e) => {
            handle_clap_error(e, robot_mode_early, &correction_result);
        }
    };
    let robot_mode = cli.is_robot_mode();

    let logging_config = lore::Config::load(cli.config.as_deref())
        .map(|c| c.logging)
        .unwrap_or_default();

    let log_dir = get_log_dir(logging_config.log_dir.as_deref());
    if logging_config.file_logging && logging_config.retention_days > 0 {
        logging::cleanup_old_logs(&log_dir, logging_config.retention_days);
    }

    let stderr_filter = logging::build_stderr_filter(cli.verbose, cli.quiet);
    let metrics_layer = MetricsLayer::new();

    let registry = tracing_subscriber::registry();

    let _file_guard: Option<tracing_appender::non_blocking::WorkerGuard>;

    if cli.log_format == "json" {
        let stderr_layer = tracing_subscriber::fmt::layer()
            .json()
            .with_writer(lore::cli::progress::SuspendingWriter)
            .with_filter(stderr_filter);

        if logging_config.file_logging {
            let file_filter = logging::build_file_filter();
            std::fs::create_dir_all(&log_dir).ok();
            let file_appender = tracing_appender::rolling::daily(&log_dir, "lore");
            let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
            _file_guard = Some(guard);
            let file_layer = tracing_subscriber::fmt::layer()
                .json()
                .with_writer(non_blocking)
                .with_filter(file_filter);
            registry
                .with(stderr_layer)
                .with(file_layer)
                .with(metrics_layer.clone())
                .init();
        } else {
            _file_guard = None;
            registry
                .with(stderr_layer)
                .with(metrics_layer.clone())
                .init();
        }
    } else {
        let stderr_layer = tracing_subscriber::fmt::layer()
            .event_format(logging::CompactHumanFormat)
            .with_writer(lore::cli::progress::SuspendingWriter)
            .with_filter(stderr_filter);

        if logging_config.file_logging {
            let file_filter = logging::build_file_filter();
            std::fs::create_dir_all(&log_dir).ok();
            let file_appender = tracing_appender::rolling::daily(&log_dir, "lore");
            let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
            _file_guard = Some(guard);
            let file_layer = tracing_subscriber::fmt::layer()
                .json()
                .with_writer(non_blocking)
                .with_filter(file_filter);
            registry
                .with(stderr_layer)
                .with(file_layer)
                .with(metrics_layer.clone())
                .init();
        } else {
            _file_guard = None;
            registry
                .with(stderr_layer)
                .with(metrics_layer.clone())
                .init();
        }
    }

    // Icon mode is independent of color flags; robot mode still defaults to ASCII.
    let glyphs = GlyphMode::detect(cli.icons.as_deref(), robot_mode);

    if std::env::var("NO_COLOR").is_ok_and(|v| !v.is_empty()) {
        LoreRenderer::init(ColorMode::Never, glyphs);
        console::set_colors_enabled(false);
    } else {
        match cli.color.as_str() {
            "never" => {
                LoreRenderer::init(ColorMode::Never, glyphs);
                console::set_colors_enabled(false);
            }
            "always" => {
                LoreRenderer::init(ColorMode::Always, glyphs);
                console::set_colors_enabled(true);
            }
            "auto" => {
                LoreRenderer::init(ColorMode::Auto, glyphs);
            }
            other => {
                LoreRenderer::init(ColorMode::Auto, glyphs);
                eprintln!("Warning: unknown color mode '{}', using auto", other);
            }
        }
    }

    let quiet = cli.quiet;

    let result = match cli.command {
        // Phase 2: Handle no-args case - in robot mode, output robot-docs; otherwise show help
        None => {
            if robot_mode {
                handle_robot_docs(robot_mode, false)
            } else {
                use clap::CommandFactory;
                let mut cmd = Cli::command();
                cmd.print_help().ok();
                println!();
                Ok(())
            }
        }
        Some(Commands::Issues(args)) => handle_issues(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Mrs(args)) => handle_mrs(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Notes(args)) => handle_notes(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Search(args)) => {
            handle_search(cli.config.as_deref(), args, robot_mode).await
        }
        Some(Commands::Timeline(args)) => {
            handle_timeline(cli.config.as_deref(), args, robot_mode).await
        }
        Some(Commands::Who(args)) => handle_who(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Me(args)) => handle_me(cli.config.as_deref(), args, robot_mode),
        Some(Commands::FileHistory(args)) => {
            handle_file_history(cli.config.as_deref(), args, robot_mode)
        }
        Some(Commands::Trace(args)) => handle_trace(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Cron(args)) => handle_cron(cli.config.as_deref(), args, robot_mode),
        Some(Commands::Token(args)) => handle_token(cli.config.as_deref(), args, robot_mode).await,
        Some(Commands::Explain {
            entity_type,
            iid,
            project,
            sections,
            no_timeline,
            max_decisions,
            since,
        }) => handle_explain(
            cli.config.as_deref(),
            &entity_type,
            iid,
            project.as_deref(),
            sections,
            no_timeline,
            max_decisions,
            since.as_deref(),
            robot_mode,
        ),
        Some(Commands::Drift {
            entity_type,
            iid,
            threshold,
            project,
        }) => {
            handle_drift(
                cli.config.as_deref(),
                &entity_type,
                iid,
                threshold,
                project.as_deref(),
                robot_mode,
            )
            .await
        }
        Some(Commands::Related {
            query_or_type,
            iid,
            limit,
            project,
        }) => {
            handle_related(
                cli.config.as_deref(),
                &query_or_type,
                iid,
                limit,
                project.as_deref(),
                robot_mode,
            )
            .await
        }
        Some(Commands::Stats(args)) => handle_stats(cli.config.as_deref(), args, robot_mode).await,
        Some(Commands::Embed(args)) => handle_embed(cli.config.as_deref(), args, robot_mode, &rt_handle).await,
        Some(Commands::Sync(args)) => {
            handle_sync_cmd(cli.config.as_deref(), args, robot_mode, &metrics_layer, &rt_handle).await
        }
        Some(Commands::Ingest(args)) => {
            handle_ingest(
                cli.config.as_deref(),
                args,
                robot_mode,
                quiet,
                &metrics_layer,
                &rt_handle,
            )
            .await
        }
        Some(Commands::Count(args)) => handle_count(cli.config.as_deref(), args, robot_mode).await,
        Some(Commands::Status) => handle_sync_status_cmd(cli.config.as_deref(), robot_mode).await,
        Some(Commands::Auth) => handle_auth_test(cli.config.as_deref(), robot_mode).await,
        Some(Commands::Doctor) => handle_doctor(cli.config.as_deref(), robot_mode).await,
        Some(Commands::Version) => handle_version(robot_mode),
        Some(Commands::Completions { shell }) => handle_completions(&shell),
        Some(Commands::Init {
            refresh,
            force,
            non_interactive,
            gitlab_url,
            token_env_var,
            projects,
            default_project,
        }) => {
            handle_init(
                cli.config.as_deref(),
                refresh,
                force,
                non_interactive,
                robot_mode,
                gitlab_url,
                token_env_var,
                projects,
                default_project,
            )
            .await
        }
        Some(Commands::GenerateDocs(args)) => {
            handle_generate_docs(cli.config.as_deref(), args, robot_mode).await
        }
        Some(Commands::Backup) => handle_backup(robot_mode),
        Some(Commands::Reset { yes: _ }) => handle_reset(robot_mode),
        Some(Commands::Migrate) => handle_migrate(cli.config.as_deref(), robot_mode).await,
        Some(Commands::Health) => handle_health(cli.config.as_deref(), robot_mode).await,
        Some(Commands::RobotDocs { brief }) => handle_robot_docs(robot_mode, brief),

        Some(Commands::List {
            entity,
            limit,
            project,
            state,
            author,
            assignee,
            label,
            milestone,
            since,
            due_before,
            has_due_date,
            sort,
            order,
            open,
            draft,
            no_draft,
            reviewer,
            target_branch,
            source_branch,
        }) => {
            if robot_mode {
                eprintln!(
                    r#"{{"warning":{{"type":"DEPRECATED","message":"'lore list' is deprecated, use 'lore issues' or 'lore mrs'","successor":"issues / mrs"}}}}"#
                );
            } else {
                eprintln!(
                    "{}",
                    Theme::warning().render(
                        "warning: 'lore list' is deprecated, use 'lore issues' or 'lore mrs'"
                    )
                );
            }
            handle_list_compat(
                cli.config.as_deref(),
                &entity,
                limit,
                project.as_deref(),
                state.as_deref(),
                author.as_deref(),
                assignee.as_deref(),
                label.as_deref(),
                milestone.as_deref(),
                since.as_deref(),
                due_before.as_deref(),
                has_due_date,
                &sort,
                &order,
                open,
                robot_mode,
                draft,
                no_draft,
                reviewer.as_deref(),
                target_branch.as_deref(),
                source_branch.as_deref(),
            )
            .await
        }
        Some(Commands::AuthTest) => {
            if robot_mode {
                eprintln!(
                    r#"{{"warning":{{"type":"DEPRECATED","message":"'lore auth-test' is deprecated, use 'lore auth'","successor":"auth"}}}}"#
                );
            } else {
                eprintln!(
                    "{}",
                    Theme::warning()
                        .render("warning: 'lore auth-test' is deprecated, use 'lore auth'")
                );
            }
            handle_auth_test(cli.config.as_deref(), robot_mode).await
        }
        Some(Commands::SyncStatus) => {
            if robot_mode {
                eprintln!(
                    r#"{{"warning":{{"type":"DEPRECATED","message":"'lore sync-status' is deprecated, use 'lore status'","successor":"status"}}}}"#
                );
            } else {
                eprintln!(
                    "{}",
                    Theme::warning()
                        .render("warning: 'lore sync-status' is deprecated, use 'lore status'")
                );
            }
            handle_sync_status_cmd(cli.config.as_deref(), robot_mode).await
        }
    };

    if let Err(e) = result {
        handle_error(e, robot_mode);
    }
    }); // rt.block_on
}

include!("app/dispatch.rs");
