use std::collections::HashMap;

use crate::features::FeatureLayer;

/// Generate a combined Dockerfile from base + features + env + user.
pub fn generate(
    base_dockerfile: &str,
    feature_layers: &[FeatureLayer],
    container_env: &HashMap<String, String>,
    remote_user: Option<&str>,
) -> String {
    let mut sections: Vec<String> = Vec::new();

    sections.push("# Generated by fabro-devcontainer".to_string());
    sections.push(base_dockerfile.to_string());

    for layer in feature_layers {
        sections.push(layer.dockerfile_snippet.clone());
    }

    if !container_env.is_empty() {
        let mut keys: Vec<&String> = container_env.keys().collect();
        keys.sort();
        let env_lines: Vec<String> = keys
            .iter()
            .map(|k| format!("ENV {}={}", k, container_env[*k]))
            .collect();
        sections.push(env_lines.join("\n"));
    }

    if let Some(user) = remote_user {
        sections.push(format!("USER {}", user));
    }

    let mut result = sections.join("\n\n");
    result.push('\n');
    result
}

#[cfg(test)]
mod tests {
    use super::*;

    fn make_layer(id: &str, dir_name: &str, snippet: &str) -> FeatureLayer {
        FeatureLayer {
            id: id.to_string(),
            dir_name: dir_name.to_string(),
            dockerfile_snippet: snippet.to_string(),
        }
    }

    #[test]
    fn base_image_only() {
        let result = generate("FROM ubuntu:22.04", &[], &HashMap::new(), None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM ubuntu:22.04
        ");
    }

    #[test]
    fn base_dockerfile_preserved_as_is() {
        let base = "FROM ubuntu:22.04\nRUN apt-get update\nRUN apt-get install -y curl";
        let result = generate(base, &[], &HashMap::new(), None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM ubuntu:22.04
        RUN apt-get update
        RUN apt-get install -y curl
        ");
    }

    #[test]
    fn with_feature_layers() {
        let layers = vec![
            make_layer("node", "node-1", "RUN install-node.sh"),
            make_layer("python", "python-1", "RUN install-python.sh"),
        ];
        let result = generate("FROM ubuntu:22.04", &layers, &HashMap::new(), None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM ubuntu:22.04

        RUN install-node.sh

        RUN install-python.sh
        ");
    }

    #[test]
    fn with_env_sorted() {
        let mut env = HashMap::new();
        env.insert("ZEBRA".to_string(), "stripes".to_string());
        env.insert("APPLE".to_string(), "red".to_string());
        env.insert("MANGO".to_string(), "yellow".to_string());
        let result = generate("FROM alpine", &[], &env, None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine

        ENV APPLE=red
        ENV MANGO=yellow
        ENV ZEBRA=stripes
        ");
    }

    #[test]
    fn with_remote_user() {
        let result = generate("FROM alpine", &[], &HashMap::new(), Some("vscode"));
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine

        USER vscode
        ");
    }

    #[test]
    fn all_combined() {
        let layers = vec![make_layer("node", "node-1", "RUN install-node.sh")];
        let mut env = HashMap::new();
        env.insert("PATH".to_string(), "/usr/local/bin".to_string());
        env.insert("HOME".to_string(), "/home/vscode".to_string());
        let result = generate("FROM ubuntu:22.04", &layers, &env, Some("vscode"));
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM ubuntu:22.04

        RUN install-node.sh

        ENV HOME=/home/vscode
        ENV PATH=/usr/local/bin

        USER vscode
        ");
    }

    #[test]
    fn empty_feature_layers_no_extra_blank_lines() {
        let result = generate("FROM alpine", &[], &HashMap::new(), Some("dev"));
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine

        USER dev
        ");
    }

    #[test]
    fn empty_env_map_treated_as_none() {
        let env = HashMap::new();
        let result = generate("FROM alpine", &[], &env, None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine
        ");
    }

    #[test]
    fn multiline_base_dockerfile() {
        let base = "FROM ubuntu:22.04 AS builder\n\
                     RUN apt-get update && apt-get install -y build-essential\n\
                     COPY . /app\n\
                     RUN make\n\
                     \n\
                     FROM ubuntu:22.04\n\
                     COPY --from=builder /app/bin /usr/local/bin";
        let result = generate(base, &[], &HashMap::new(), None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM ubuntu:22.04 AS builder
        RUN apt-get update && apt-get install -y build-essential
        COPY . /app
        RUN make

        FROM ubuntu:22.04
        COPY --from=builder /app/bin /usr/local/bin
        ");
    }

    #[test]
    fn container_env_only() {
        let mut cenv = HashMap::new();
        cenv.insert("DEBIAN_FRONTEND".to_string(), "noninteractive".to_string());
        let result = generate("FROM alpine", &[], &cenv, None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine

        ENV DEBIAN_FRONTEND=noninteractive
        ");
    }

    #[test]
    fn container_env_with_multiple_keys() {
        let mut cenv = HashMap::new();
        cenv.insert("ALPHA".to_string(), "first".to_string());
        cenv.insert("BETA".to_string(), "second".to_string());
        cenv.insert("GAMMA".to_string(), "third".to_string());
        let result = generate("FROM alpine", &[], &cenv, None);
        insta::assert_snapshot!(result, @r"
        # Generated by fabro-devcontainer

        FROM alpine

        ENV ALPHA=first
        ENV BETA=second
        ENV GAMMA=third
        ");
    }
}
