use std::time::Instant;

use anyhow::Result;
use clap::Parser;

mod cli;
mod input;
mod inspect;
mod output;
mod query;

use crate::cli::{Cli, Commands};

fn main() -> Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Commands::Query(cmd) => {
            let sql = query::load_query(&cmd)?;
            let params = query::parse_query_params(&cmd.params)?;
            let type_inference_options = query::resolve_type_inference_options(&cmd);
            let input_normalization_options = query::resolve_input_normalization_options(&cmd);
            let extraction_options = query::resolve_extraction_options(&cmd);
            let input_specs = input::parse_input_specs(&cmd.input, None)?;
            let workbook_inputs = input_specs
                .iter()
                .map(|input| query_forge::WorkbookInput {
                    path: input.path.as_path(),
                    sheet_name: input.sheet_name.as_deref(),
                    table_name: input.table_name.as_deref(),
                })
                .collect::<Vec<_>>();

            let start = Instant::now();
            let result =
                query_forge::run_query_with_params_multi_inputs_and_options_and_normalization(
                    &workbook_inputs,
                    &sql,
                    &params,
                    &type_inference_options,
                    &input_normalization_options,
                    &extraction_options,
                    !cmd.no_headers,
                )?;
            let elapsed = start.elapsed();

            output::write_query_result(&result, cmd.output.as_deref(), cmd.format)?;

            if cmd.meta {
                let input_tables = input_specs
                    .iter()
                    .enumerate()
                    .map(|(i, spec)| output::InputTableMeta {
                        table_name: spec.table_name.clone().unwrap_or_else(|| {
                            if i == 0 {
                                "table".to_owned()
                            } else {
                                format!("table{}", i + 1)
                            }
                        }),
                        source_path: spec.path.display().to_string(),
                        source_selector: spec.sheet_name.clone(),
                    })
                    .collect();

                let meta = output::QueryMetadata {
                    row_count: result.rows.len(),
                    columns: result.columns.clone(),
                    input_tables,
                    execution_time_ms: elapsed.as_millis(),
                };
                output::write_query_metadata(&meta, cmd.meta_format.as_ref());
            }
        }
        Commands::Tables(cmd) => inspect::run_tables_command(cmd)?,
        Commands::Schema(cmd) => inspect::run_schema_command(cmd)?,
        Commands::Inspect(cmd) => inspect::run_inspect_command(cmd)?,
    }

    Ok(())
}
