use nu_plugin::{EngineInterface, EvaluatedCall, PluginCommand, SimplePluginCommand};
use nu_protocol::{
    Category, LabeledError, Signature, Span, SyntaxShape, Type, Value, record
};
use fluent_syntax::parser::parse;
use fluent_syntax::ast::{Entry, Message, Pattern, PatternElement};
use std::fs;
use crate::FluentPlugin;

pub struct ParseFtl;

impl SimplePluginCommand for ParseFtl {
    type Plugin = FluentPlugin;

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

    fn signature(&self) -> Signature {
        Signature::build(PluginCommand::name(self))
            .input_output_type(Type::Any, Type::Record(vec![].into()))
            .required("file", SyntaxShape::Filepath, "FTL file to parse")
            .category(Category::Strings)
    }

    fn description(&self) -> &str {
        "Parse a Fluent Translation List (.ftl) file and return message structure"
    }

    fn examples(&self) -> Vec<nu_protocol::Example<'_>> {
        vec![
            nu_protocol::Example {
                description: "Parse an FTL file",
                example: "fluent-parse locales/en-US/main.ftl",
                result: None,
            },
        ]
    }

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

        // Read FTL file
        let ftl_content = fs::read_to_string(&file_path)
            .map_err(|e| LabeledError::new("Read error").with_label(format!("Failed to read file '{}': {}", file_path, e), call.head))?;

        // Parse FTL content
        let resource = match parse(ftl_content) {
            Ok(res) => res,
            Err((_, errors)) => {
                return Err(LabeledError::new("Parse error").with_label(format!("Failed to parse FTL content: {:?}", errors), call.head));
            }
        };

        // Convert AST to Nushell values
        let messages = extract_messages_from_resource(&resource, call.head)
            .map_err(|e| LabeledError::new("Extract error").with_label(e, call.head))?;

        let message_count = messages.len() as i64;
        let result = Value::record(
            record! {
                "file" => Value::string(file_path, call.head),
                "messages" => Value::list(messages, call.head),
                "message_count" => Value::int(message_count, call.head),
            },
            call.head,
        );

        Ok(result)
    }
}

fn extract_messages_from_resource(resource: &fluent_syntax::ast::Resource<String>, span: Span) -> Result<Vec<Value>, String> {
    let mut messages = Vec::new();

    for entry in &resource.body {
        if let Entry::Message(message) = entry {
            let message_value = extract_message_info(message, span)?;
            messages.push(message_value);
        }
    }

    Ok(messages)
}

fn extract_message_info(message: &Message<String>, span: Span) -> Result<Value, String> {
    let id = message.id.name.to_string();

    // Extract the default text from the pattern
    let text = if let Some(ref pattern) = message.value {
        extract_pattern_text(pattern)
    } else {
        String::new()
    };

    // Extract attributes
    let mut attributes = Vec::new();
    for attribute in &message.attributes {
        let attr_value = Value::record(
            record! {
                "name" => Value::string(attribute.id.name.to_string(), span),
                "value" => Value::string(extract_pattern_text(&attribute.value), span),
            },
            span,
        );
        attributes.push(attr_value);
    }

    // Extract comment if present
    let comment = message.comment.as_ref()
        .map(|c| c.content.iter().map(|line| line.trim()).collect::<Vec<_>>().join("\n"))
        .unwrap_or_default();

    Ok(Value::record(
        record! {
            "id" => Value::string(id, span),
            "text" => Value::string(text, span),
            "attributes" => Value::list(attributes, span),
            "comment" => Value::string(comment, span),
        },
        span,
    ))
}

fn extract_pattern_text(pattern: &Pattern<String>) -> String {
    pattern.elements
        .iter()
        .map(|element| match element {
            PatternElement::TextElement { value } => value.to_string(),
            PatternElement::Placeable { ref expression } => {
                // For now, just return placeholder representation
                format!("{{{}}}", format!("{:?}", expression).replace("\"", ""))
            }
        })
        .collect::<Vec<_>>()
        .join("")
}
