use std::{fs, path::Path};

use dsx_common::config::DsxConfig;
use rocket_dyn_templates::{Template, context};
use serde::Serialize;

use crate::{
    error::ApiError,
    model::{DATA_DIR, PackageHandle},
};

// Search for a package
#[get("/?<q>")]
pub fn search_packages(q: Option<String>) -> Result<Template, ApiError> {
    #[derive(Serialize)]
    struct Package {
        name: String,
        description: String,
        updated_at: String,
    }

    let mut packages = Vec::new();

    let dir = match fs::read_dir(DATA_DIR.join("repos")) {
        Ok(dir) => dir,
        Err(e) => {
            warn!("failed to read repos directory: {}", e);
            return Err(ApiError::InternalServerError(()));
        }
    };

    for entry in dir {
        let entry = entry.map_err(|e| {
            warn!("failed to read entry: {}", e);
            ApiError::InternalServerError(())
        })?;

        let config_path = entry.path().join("repo").join("Dsx.toml");
        if config_path.exists() {
            let text = fs::read_to_string(&config_path).map_err(|e| {
                warn!("failed to read config file: {}", e);
                ApiError::InternalServerError(())
            })?;
            let config: DsxConfig = toml::from_str(&text).map_err(|e| {
                warn!("failed to parse config file: {}", e);
                ApiError::InternalServerError(())
            })?;

            error!("{}", config.description.clone().unwrap_or_default());

            // skip repo if it doesnt match query params
            if let Some(query) = &q
                && !(config.name.contains(query)
                    || config
                        .description
                        .clone()
                        .unwrap_or_default()
                        .contains(query))
            {
                continue;
            }

            packages.push(Package {
                name: config.name,
                description: config.description.unwrap_or_default(),
                updated_at: String::from("0:00"),
            })
        }
    }

    Ok(Template::render(
        "packages",
        context! {
            packages,
            query: q.clone().unwrap_or_default()
        },
    ))
}

// Main page for a repository, shows status, files, name etc.
#[get("/<id>")]
pub fn package_main(id: &str) -> Result<Template, ApiError> {
    // get package info

    let mut handle = PackageHandle::new(id);
    let meta = handle.get_meta()?;
    let config = handle.get_config()?;
    let files = handle.file_tree("")?;

    let parent_path: Option<String> = None;
    let current_path: Option<String> = None;

    Ok(Template::render(
        "package_home",
        context! {
            parent_path,
            current_path,
            meta: meta,
            config: config,
            files: files,
        },
    ))
}

// Path for a file within a repo
#[get("/<id>/~repo/<path..>", rank = 1)]
pub fn repo_file(id: &str, path: std::path::PathBuf) -> Result<Template, ApiError> {
    // get package info
    let mut handle = PackageHandle::new(id);
    let meta = handle.get_meta()?;
    let config = handle.get_config()?;
    let files = handle.file_tree(&path)?;

    let parent_path = path.parent().unwrap_or(Path::new(""));
    let current_path = &path;

    Ok(Template::render(
        "package_home",
        context! {
            parent_path,
            current_path,
            meta: meta,
            config: config,
            files: files,
        },
    ))
}

// Search within a package's files
#[get("/<name>/~repo?<q>", rank = 2)]
pub fn search_repo_files(name: &str, q: Option<String>) -> String {
    format!("Search within {} for {:?}", name, q)
}

// Page listing repo artifacts by date
#[get("/<name>/artifacts")]
pub fn list_artifacts(name: &str) -> String {
    format!("Artifacts for package {}", name)
}

// Page for a specific artifact and status/logs
#[get("/<name>/artifacts/<id>")]
pub fn artifact_detail(name: &str, id: u64) -> String {
    format!("Artifact {} details for package {}", id, name)
}
