use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{
    Category, LabeledError, Signature, Span, SyntaxShape, Type, Value, record,
};
use fluent::{FluentBundle, FluentResource};
use unic_langid::LanguageIdentifier;
use crate::FluentPlugin;

pub struct CreateBundle;

impl SimplePluginCommand for CreateBundle {
    type Plugin = FluentPlugin;

    fn name(&self) -> &str {
        "fluent-create-bundle"
    }

    fn signature(&self) -> Signature {
        Signature::build(PluginCommand::name(self))
            .input_output_type(Type::Any, Type::Record(vec![].into()))
            .required("locale", SyntaxShape::String, "Locale code (e.g., en-US)")
            .named(
                "global",
                SyntaxShape::List(Box::new(SyntaxShape::Filepath)),
                "Global FTL files to include",
                Some('g'),
            )
            .named(
                "page",
                SyntaxShape::List(Box::new(SyntaxShape::Filepath)),
                "Page-specific FTL files to include",
                Some('p'),
            )
            .named(
                "fallback",
                SyntaxShape::List(Box::new(SyntaxShape::String)),
                "Fallback locales in order",
                Some('f'),
            )
            .switch("override", "Allow page files to override global messages", Some('o'))
            .category(Category::Strings)
    }

    fn description(&self) -> &str {
        "Create a merged Fluent bundle from global and page-specific locale files"
    }

    fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
        vec![
            nu_protocol::Example {
                description: "Create bundle with global and page-specific files",
                example: "fluent-create-bundle en-US --global [global/en-US/common.ftl] --page [pages/blog/en-US/blog.ftl]",
                result: None,
            },
            nu_protocol::Example {
                description: "Create bundle with fallback support",
                example: "fluent-create-bundle es-ES --fallback [en-US] --global [global/es-ES/common.ftl]",
                result: None,
            },
        ]
    }

    fn run(
        &self,
        _plugin: &Self::Plugin,
        _engine: &EngineInterface,
        call: &EvaluatedCall,
        _input: &Value,
    ) -> Result<Value, LabeledError> {
        let locale_code: String = call.req(0)?;

        // Parse locale
        let locale: LanguageIdentifier = locale_code.parse()
            .map_err(|e| LabeledError::new("Invalid locale").with_label(format!("Invalid locale '{}': {}", locale_code, e), call.head))?;

        // Create fallback locales
        let mut locales = vec![locale.clone()];
        if let Some(fallback_value) = call.get_flag("fallback")? {
            let fallback_codes = extract_string_list(fallback_value)?;
            for code in fallback_codes {
                let fallback_locale: LanguageIdentifier = code.parse()
                    .map_err(|e| LabeledError::new("Invalid fallback locale").with_label(format!("Invalid fallback locale '{}': {}", code, e), call.head))?;
                locales.push(fallback_locale);
            }
        }

        // Create bundle
        let mut bundle = FluentBundle::new(locales);
        let allow_override = call.has_flag("override")?;

        // Load global files first (lower priority)
        if let Some(global_value) = call.get_flag("global")? {
            let global_files = extract_file_list(global_value)?;
            load_files_to_bundle(&mut bundle, &global_files, "global")?;
        }

        // Load page files (higher priority, can override global)
        if let Some(page_value) = call.get_flag("page")? {
            let page_files = extract_file_list(page_value)?;
            if allow_override {
                // Page files can override global messages
                load_files_to_bundle(&mut bundle, &page_files, "page")?;
            } else {
                // Only add page messages that don't exist in global
                load_files_to_bundle_no_override(&mut bundle, &page_files)?;
            }
        }

        // Extract bundle information
        let bundle_info = extract_bundle_info(&bundle, &locale_code, call.head);

        Ok(bundle_info)
    }
}

fn extract_string_list(value: Value) -> Result<Vec<String>, LabeledError> {
    match value {
        Value::List { vals, .. } => {
            vals.iter()
                .map(|v| value_to_string(v))
                .collect::<Result<Vec<_>, _>>()
        }
        _ => Err(LabeledError::new("Invalid list").with_label("Must be a list of strings", nu_protocol::Span::unknown())),
    }
}

fn extract_file_list(value: Value) -> Result<Vec<String>, LabeledError> {
    extract_string_list(value)
}

fn load_files_to_bundle(
    bundle: &mut FluentBundle<FluentResource>,
    files: &[String],
    source: &str,
) -> Result<(), LabeledError> {
    for file_path in files {
        let content = std::fs::read_to_string(file_path)
            .map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read '{}': {}", file_path, e), nu_protocol::Span::unknown()))?;

        let resource = FluentResource::try_new(content)
            .map_err(|e| LabeledError::new("Invalid FTL").with_label(format!("Invalid FTL in '{}': {:?}", file_path, e), nu_protocol::Span::unknown()))?;

        bundle.add_resource(resource)
            .map_err(|errors| {
                let error_msgs: Vec<String> = errors.iter()
                    .map(|e| format!("{:?}", e))
                    .collect();
                LabeledError::new("Load error").with_label(format!(
                    "Failed to load {} file '{}': {}",
                    source,
                    file_path,
                    error_msgs.join(", ")
                ), nu_protocol::Span::unknown())
            })?;
    }
    Ok(())
}

fn load_files_to_bundle_no_override(
    bundle: &mut FluentBundle<FluentResource>,
    files: &[String],
) -> Result<(), LabeledError> {
    // This is a simplified implementation - in practice, we'd need to
    // check for conflicts before adding resources
    load_files_to_bundle(bundle, files, "page")
}

fn extract_bundle_info(
    _bundle: &FluentBundle<FluentResource>,
    locale_code: &str,
    span: Span,
) -> Value {
    // Extract message IDs and basic info from the bundle
    let message_ids = Vec::new();
    let message_count = 0;

    // This is a simplified extraction - the actual FluentBundle API
    // doesn't directly expose message enumeration, so in practice
    // we'd need to track this during bundle creation

    Value::record(
        record! {
            "locale" => Value::string(locale_code.to_string(), span),
            "message_count" => Value::int(message_count, span),
            "message_ids" => Value::list(message_ids, span),
            "bundle_type" => Value::string("merged".to_string(), span),
        },
        span,
    )
}

fn value_to_string(value: &Value) -> Result<String, LabeledError> {
    match value {
        Value::String { val, .. } => Ok(val.clone()),
        Value::Int { val, .. } => Ok(val.to_string()),
        Value::Float { val, .. } => Ok(val.to_string()),
        Value::Bool { val, .. } => Ok(val.to_string()),
        _ => Err(LabeledError::new("Type conversion error").with_label(
            format!("Cannot convert {:?} to string", value.get_type()),
            nu_protocol::Span::unknown()
        )),
    }
}