use serde::Deserialize;
use serde_json::json;
use std::io::{Read, Write};

use super::{common, ToolResult};

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ImportParams {
    pub env_file: String,
    #[serde(default)]
    pub delete_after: bool,
}

pub async fn execute(params: ImportParams) -> ToolResult {
    common::require_trimmed_non_empty(&params.env_file, "env_file")?;
    common::validate_bounded_string(&params.env_file, "env_file", common::MAX_STRING_LENGTH)?;
    if crate::settings::plaintext_import_disabled() {
        return Err(crate::settings::IMPORT_DISABLED_MESSAGE.to_string());
    }
    let env_path = common::validate_path_within_cwd(&params.env_file, "env_file")?;
    let mut content = String::new();
    let mut input = common::open_readonly_nofollow(&env_path, "env_file")?;
    input
        .read_to_string(&mut content)
        .map_err(|e| format!("failed to read env_file: {e}"))?;
    let vault = envseal::vault::Vault::open_default().map_err(|e| e.to_string())?;
    let mut mappings = Vec::new();
    let mut imported = 0_usize;
    let mut skipped = Vec::new();

    for (index, line) in content.lines().enumerate() {
        let trimmed = line.trim();
        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }
        let parts: Vec<&str> = trimmed.splitn(2, '=').collect();
        if parts.len() != 2 || parts[0].trim().is_empty() {
            skipped.push(format!("line {}: malformed assignment", index + 1));
            continue;
        }
        let env_var = parts[0].trim().to_string();
        common::validate_bounded_string(&env_var, "env var", common::MAX_STRING_LENGTH)?;
        let value = parts[1]
            .trim()
            .trim_matches('"')
            .trim_matches('\'')
            .to_string();
        common::validate_bounded_string(&value, "secret value", common::MAX_STRING_LENGTH)?;
        if value.is_empty() {
            skipped.push(format!("line {}: empty value for {env_var}", index + 1));
            continue;
        }
        let secret_name = envseal::envseal_file::env_var_to_secret_name(&env_var);
        common::validate_secret_name(&secret_name, "secret_name")?;
        vault
            .store(&secret_name, value.as_bytes(), false)
            .map_err(|e| format!("failed to import {env_var}: {e}"))?;
        imported += 1;
        mappings.push((env_var, secret_name));
    }

    let output_path = if env_path.file_name().and_then(|f| f.to_str()) == Some(".env") {
        env_path.with_file_name(".envseal")
    } else {
        env_path.with_extension("envseal")
    };
    let mut out = String::from("# .envseal -- generated by envseal-mcp\n");
    for (env_var, secret_name) in &mappings {
        out.push_str(&format!("{env_var}={secret_name}\n"));
    }
    let tmp_path = output_path.with_extension(format!(
        "tmp-{}-{}",
        std::process::id(),
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map_err(|e| e.to_string())?
            .as_nanos()
    ));
    let write_result = (|| -> Result<(), String> {
        let mut opts = std::fs::OpenOptions::new();
        opts.write(true).create_new(true);
        #[cfg(unix)]
        {
            use std::os::unix::fs::OpenOptionsExt;
            opts.mode(0o600).custom_flags(libc::O_NOFOLLOW);
        }
        let mut tmp = opts
            .open(&tmp_path)
            .map_err(|e| format!("failed to create temporary output file: {e}"))?;
        tmp.write_all(out.as_bytes())
            .map_err(|e| format!("failed to write output file: {e}"))?;
        tmp.sync_all()
            .map_err(|e| format!("failed to sync output file: {e}"))?;
        std::fs::rename(&tmp_path, &output_path)
            .map_err(|e| format!("failed to finalize output file: {e}"))?;
        Ok(())
    })();
    if write_result.is_err() {
        let _ = std::fs::remove_file(&tmp_path);
    }
    write_result?;

    let deleted_source = if params.delete_after {
        std::fs::remove_file(&env_path).is_ok()
    } else {
        false
    };

    Ok(json!({
        "imported": imported,
        "mappings": mappings.len(),
        "skipped": skipped,
        "output_file": output_path,
        "deleted_source": deleted_source,
    }))
}
