use std::{num::NonZeroU64, path::Path, process::ExitCode};

use anyhow::Result;
use log::{debug, error, info, warn};

mod args;
mod nvim;
mod platform;

fn main() -> ExitCode {
    env_logger::builder()
        .format_timestamp(None)
        .filter_level(log::LevelFilter::Info)
        .parse_default_env()
        .init();

    let Ok(args) = args::parse() else {
        let mut args = std::env::args();
        let program_name = args.next();
        let program_name = program_name.as_deref().unwrap_or("launch-editor-nvim");

        warn!("Usage: {program_name} <filename> [line] [column]");
        return ExitCode::FAILURE;
    };

    match edit_with_nvim(&args.filename, args.line, args.col) {
        Ok(code) => code,
        Err(e) => {
            error!("{e}");
            ExitCode::FAILURE
        }
    }
}

fn edit_with_nvim(
    path: &Path,
    line: Option<NonZeroU64>,
    col: Option<NonZeroU64>,
) -> Result<ExitCode> {
    if log::log_enabled!(log::Level::Info) {
        let path = path.display();
        match (line, col) {
            (Some(line), Some(col)) => info!("Opening {path}:{line}:{col}"),
            (Some(line), _) => info!("Opening {path}:{line}"),
            _ => info!("Opening {path}"),
        }
    }

    let path = path.canonicalize()?;
    let best_nvim = nvim::instances()?
        .filter_map(|nvim| {
            let pid = nvim.process.pid();
            let cwd = nvim.process.current_dir();

            debug!("Neovim {pid}: cwd={}", cwd.display());
            path.strip_prefix(cwd)
                .inspect_err(|_| debug!("Neovim {pid} rejected: file outside cwd"))
                .ok()
                .zip(Some(nvim))
        })
        .min_by_key(|(suffix, nvim)| {
            let n = suffix.components().count();
            debug!(
                "Neovim {pid} candidate: score = {n} (lower is better)",
                pid = nvim.process.pid()
            );
            n
        })
        .map(|(_, nvim)| nvim);

    match best_nvim {
        Some(nvim) => {
            nvim.edit(&path, line, col)?;
            Ok(ExitCode::SUCCESS)
        }
        None => {
            warn!("No suitable Neovim instance was found!");
            info!("tip: the path must be within Neovim's current working directory");
            info!("tip: try again with RUST_LOG=debug");
            Ok(ExitCode::FAILURE)
        }
    }
}
