use nu_engine::command_prelude::*;

#[derive(Clone)]
pub struct FromUrl;

impl Command for FromUrl {
    fn name(&self) -> &str {
        "from url"
    }

    fn signature(&self) -> Signature {
        Signature::build("from url")
            .input_output_types(vec![(Type::String, Type::record())])
            .category(Category::Formats)
    }

    fn description(&self) -> &str {
        "Parse url-encoded string as a record."
    }

    fn run(
        &self,
        _engine_state: &EngineState,
        _stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let head = call.head;
        from_url(input, head)
    }

    fn examples(&self) -> Vec<Example<'_>> {
        vec![Example {
            example: "'bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter' | from url",
            description: "Convert url encoded string into a record",
            result: Some(Value::test_record(record! {
                "bread" =>  Value::test_string("baguette"),
                "cheese" => Value::test_string("comté"),
                "meat" =>   Value::test_string("ham"),
                "fat" =>    Value::test_string("butter"),
            })),
        }]
    }
}

fn from_url(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
    let (concat_string, span, metadata) = input.collect_string_strict(head)?;

    let result = serde_urlencoded::from_str::<Vec<(String, String)>>(&concat_string);

    match result {
        Ok(result) => {
            let record = result
                .into_iter()
                .map(|(k, v)| (k, Value::string(v, head)))
                .collect();

            Ok(PipelineData::value(Value::record(record, head), metadata))
        }
        _ => Err(ShellError::UnsupportedInput {
            msg: "String not compatible with URL encoding".to_string(),
            input: "value originates from here".into(),
            msg_span: head,
            input_span: span,
        }),
    }
}

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

    #[test]
    fn test_examples() -> nu_test_support::Result {
        nu_test_support::test().examples(FromUrl)
    }
}
