mod blacklist;
mod build;
mod changelog;
mod clean;
mod colors;
mod config;
mod deps;
mod download;
mod fetch;
mod info;
mod install;
mod list;
mod load_data;
mod options;
mod parse;
mod process;
mod remove;
mod requires;
mod reverse;
mod search;
mod show_config;
mod stats;
mod update;
mod upgrade;
mod utils;

use crate::colors::get_colors;
use crate::config::get_config;
use crate::options::Options;
use clap::{Parser, Subcommand};
use fs2::FileExt;
use std::fs::File;
use std::process::exit;

#[derive(Parser)]
#[command(
    name = "sbpkg",
    version,
    about = "SlackBuilds.org package manager for Slackware"
)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    #[command(about = "Sync the SlackBuilds repository")]
    Update {
        #[arg(long, help = "Generate data.json from existing repository")]
        generate: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Upgrade installed packages")]
    Upgrade {
        #[arg(long, help = "Check for upgradable packages")]
        check: bool,
        #[arg(long, help = "Skip installed packages")]
        skip_installed: bool,
        #[arg(long, help = "Skip packages with version ahead of repository")]
        skip_ahead: bool,
        #[arg(long, help = "Switch off resolving dependencies")]
        no_deps: bool,
        #[arg(long, help = "Switch off checksum verification")]
        no_checksum: bool,
        #[arg(long, help = "Enable GPG signature verification")]
        gpg: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Build packages without install")]
    Build {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Skip installed packages")]
        skip_installed: bool,
        #[arg(long, help = "Switch off resolving dependencies")]
        no_deps: bool,
        #[arg(long, help = "Switch off checksum verification")]
        no_checksum: bool,
        #[arg(long = "gpg", help = "Enable GPG signature verification")]
        gpg: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Build and install packages")]
    Install {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Skip installed packages")]
        skip_installed: bool,
        #[arg(long, help = "Switch off resolving dependencies")]
        no_deps: bool,
        #[arg(long, help = "Switch off checksum verification")]
        no_checksum: bool,
        #[arg(long = "gpg", help = "Enable GPG signature verification")]
        gpg: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Build and reinstall packages")]
    Reinstall {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Switch off resolving dependencies")]
        no_deps: bool,
        #[arg(long, help = "Switch off checksum verification")]
        no_checksum: bool,
        #[arg(long = "gpg", help = "Enable GPG signature verification")]
        gpg: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Download only sources and copy SlackBuild scripts")]
    Fetch {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Switch off checksum verification")]
        no_checksum: bool,
        #[arg(long = "gpg", help = "Enable GPG signature verification")]
        gpg: bool,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },

    #[command(about = "Display statistics for the local SBo repository")]
    Stats,

    #[command(about = "Remove installed packages")]
    Remove {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
        #[arg(
            long,
            help = "Also remove dependencies installed alongside the package"
        )]
        with_deps: bool,
    },

    #[command(about = "List of the package dependencies")]
    Deps {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
    },

    #[command(about = "Show packages that depend on the given package(s)")]
    Reverse {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
    },

    #[command(about = "Display information about the packages")]
    Info {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "Display a file from package directory")]
        read_file: Option<String>,
    },

    #[command(about = "Matching for packages in the repository")]
    Search {
        #[arg(required = true, num_args = 1..)]
        packages: Vec<String>,
        #[arg(long, help = "List packages by maintainer")]
        maintainer: bool,
        #[arg(long, help = "List packages by category")]
        category: bool,
    },

    #[command(about = "List installed SBo packages")]
    List {
        #[arg(required = false)]
        filter: Vec<String>,
    },

    #[command(about = "Display the changelog")]
    Changelog {
        #[arg(required = false)]
        filter: Vec<String>,
    },

    #[command(about = "Clean the build directory")]
    Clean {
        #[arg(long, help = "Ignore any message confirmation")]
        no_confirm: bool,
    },
    #[command(about = "Display configuration file")]
    Config,
}

/// Entry point for the `sbpkg` CLI.
///
/// Parses the subcommand and dispatches to the appropriate handler.
#[allow(clippy::too_many_lines)]
fn main() {
    let cli = Cli::parse();
    let cfg = get_config();
    let color = get_colors(cfg.colors);

    let _lock = if cfg.lock {
        let lock_file = File::create("/var/run/sbpkg.lock").unwrap_or_else(|e| {
            eprintln!(
                "{}Error:{} cannot create lock file: {}",
                color.red, color.reset, e
            );
            exit(1);
        });

        if lock_file.try_lock_exclusive().is_err() {
            eprintln!(
                "{}Error:{} sbpkg is already running",
                color.red, color.reset
            );
            exit(1);
        }

        Some(lock_file)
    } else {
        None
    };

    match cli.command {
        Commands::Update {
            generate,
            no_confirm,
        } => update::update(&Options {
            generate,
            no_confirm,
            ..Default::default()
        }),

        Commands::Upgrade {
            check,
            skip_installed,
            skip_ahead,
            no_deps,
            no_checksum,
            gpg,
            no_confirm,
        } => upgrade::upgrade(
            &Options {
                check,
                skip_installed,
                skip_ahead,
                no_deps,
                no_checksum,
                gpg,
                no_confirm,
                ..Default::default()
            },
            "upgrade",
        ),

        Commands::Build {
            packages,
            skip_installed,
            no_deps,
            no_checksum,
            gpg,
            no_confirm,
        } => {
            process::process(
                &packages,
                &Options {
                    skip_installed,
                    no_deps,
                    no_checksum,
                    gpg,
                    no_confirm,
                    ..Default::default()
                },
                "build",
            );
        }

        Commands::Install {
            packages,
            skip_installed,
            no_deps,
            no_checksum,
            gpg,
            no_confirm,
        } => process::process(
            &packages,
            &Options {
                skip_installed,
                no_deps,
                no_checksum,
                gpg,
                no_confirm,
                ..Default::default()
            },
            "install",
        ),

        Commands::Reinstall {
            packages,
            no_deps,
            no_checksum,
            gpg,
            no_confirm,
        } => process::process(
            &packages,
            &Options {
                no_deps,
                no_checksum,
                gpg,
                no_confirm,
                ..Default::default()
            },
            "reinstall",
        ),

        Commands::Fetch {
            packages,
            no_checksum,
            gpg,
            no_confirm,
        } => fetch::fetch(
            &packages,
            &Options {
                no_checksum,
                gpg,
                no_confirm,
                ..Default::default()
            },
        ),

        Commands::Stats => stats::stats(),

        Commands::Remove {
            packages,
            no_confirm,
            with_deps,
        } => remove::remove(
            &packages,
            &Options {
                no_confirm,
                with_deps,
                ..Default::default()
            },
        ),

        Commands::Deps { packages } => deps::deps(&packages),

        Commands::Reverse { packages } => reverse::reverse(&packages),

        Commands::Info {
            packages,
            read_file,
        } => info::info(
            &packages,
            &Options {
                read_file,
                ..Default::default()
            },
        ),

        Commands::Search {
            packages,
            maintainer,
            category,
        } => search::search(
            &packages,
            &Options {
                category,
                maintainer,
                ..Default::default()
            },
        ),

        Commands::List { filter } => list::list(&filter),

        Commands::Changelog { filter } => changelog::changelog(&filter),

        Commands::Clean { no_confirm } => clean::clean(&Options {
            no_confirm,
            ..Default::default()
        }),

        Commands::Config => show_config::show_config(),
    }
}
