Documentation
use std::rc::Rc;

use crate::{
    functions_definitions::{Arguments, Example, FunctionDefinitions},
    json_value::JsonValue,
    processor::Context,
    selection::Get,
};

use indexmap::IndexMap;

pub fn get() -> FunctionDefinitions {
    FunctionDefinitions::new("sub", 3, 3, |args| {
        struct Impl(Vec<Rc<dyn Get>>);
        impl Get for Impl {
            fn get(&self, value: &Context) -> Option<JsonValue> {
                let start = if let Some(JsonValue::Number(n)) = self.0.apply(value, 1) {
                    if let Ok(start) = TryInto::<usize>::try_into(n) {
                        start
                    } else {
                        return None;
                    }
                } else {
                    return None;
                };
                let length = if let Some(JsonValue::Number(n)) = self.0.apply(value, 2) {
                    if let Ok(start) = TryInto::<usize>::try_into(n) {
                        start
                    } else {
                        return None;
                    }
                } else {
                    return None;
                };
                match self.0.apply(value, 0) {
                    Some(JsonValue::Object(map)) => {
                        let mut new_map = IndexMap::with_capacity(length);
                        for (index, (k, v)) in map.into_iter().enumerate() {
                            if new_map.len() == length {
                                break;
                            }
                            if index >= start {
                                new_map.insert(k, v);
                            }
                        }
                        Some(new_map.into())
                    }
                    Some(JsonValue::Array(vec)) => {
                        let mut new_vec = Vec::with_capacity(length);
                        for (index, i) in vec.into_iter().enumerate() {
                            if new_vec.len() == length {
                                break;
                            }
                            if index >= start {
                                new_vec.push(i);
                            }
                        }
                        Some(new_vec.into())
                    }
                    Some(JsonValue::String(str)) => {
                        if start >= str.len() || length == 0 {
                            Some(String::new().into())
                        } else {
                            let last_index = start + length;
                            let last_index = if last_index >= str.len() {
                                str.len()
                            } else {
                                last_index
                            };
                            let str = str[start..last_index].to_string();
                            Some(str.into())
                        }
                    }
                    _ => None,
                }
            }
        }
        Rc::new(Impl(args))
    })
        .add_description_line(
            "If the first argument is a list, creates a new list that start from the second arguments and has the size of the third argument."
        )
        .add_description_line(
            "If the first argument is an object, creates a new object that start from the second arguments and has the size of the third argument."
        )
        .add_description_line(
            "If the first argument is a string, creates a substring that start from the second arguments and has the size of the third argument."
        )
        .add_example(
            Example::new()
                .add_argument("[1, 2, 3, 4, 5, 6]")
                .add_argument("2")
                .add_argument("3")
                .expected_output("[3, 4, 5]")
        )
        .add_example(
            Example::new()
                .add_argument("[1, 2, 3, 4]")
                .add_argument("6")
                .add_argument("10")
                .expected_output("[]")
        )
        .add_example(
            Example::new()
                .add_argument("[1, 2, 3, 4]")
                .add_argument("0")
                .add_argument("10")
                .expected_output("[1, 2, 3, 4]")
        )
        .add_example(
            Example::new()
                .add_argument("[1, 2, 3, 4]")
                .add_argument("1")
                .add_argument("10")
                .expected_output("[2, 3, 4]")
        )
        .add_example(
            Example::new()
                .add_argument(
                    "{\"key-1\": 1, \"key-2\": 2, \"key-3\": 3, \"key-4\": 4, \"key-5\": 5, \"key-6\": 6}"
                )
                .add_argument("1")
                .add_argument("2")
                .expected_output("{\"key-2\": 2, \"key-3\": 3}")
        )
        .add_example(
            Example::new()
                .add_argument(
                    "{\"key-1\": 1, \"key-2\": 2, \"key-3\": 3, \"key-4\": 4, \"key-5\": 5, \"key-6\": 6}"
                )
                .add_argument("0")
                .add_argument("2")
                .expected_output("{\"key-1\": 1, \"key-2\": 2}")
        )
        .add_example(
            Example::new()
                .add_argument(
                    "{\"key-1\": 1, \"key-2\": 2, \"key-3\": 3, \"key-4\": 4, \"key-5\": 5, \"key-6\": 6}"
                )
                .add_argument("4")
                .add_argument("10")
                .expected_output("{\"key-5\": 5, \"key-6\": 6}")
        )
        .add_example(
            Example::new()
                .add_argument(
                    "{\"key-1\": 1, \"key-2\": 2, \"key-3\": 3, \"key-4\": 4, \"key-5\": 5, \"key-6\": 6}"
                )
                .add_argument("20")
                .add_argument("10")
                .expected_output("{}")
        )
        .add_example(
            Example::new()
                .add_argument("\"123456\"")
                .add_argument("1")
                .add_argument("3")
                .expected_output("\"234\"")
        )
        .add_example(
            Example::new()
                .add_argument("\"123456\"")
                .add_argument("1")
                .add_argument("30")
                .expected_output("\"23456\"")
        )
        .add_example(
            Example::new()
                .add_argument("\"123456\"")
                .add_argument("0")
                .add_argument("30")
                .expected_output("\"123456\"")
        )
        .add_example(
            Example::new()
                .add_argument("\"123456\"")
                .add_argument("20")
                .add_argument("30")
                .expected_output("\"\"")
        )
        .add_example(
            Example::new()
                .add_argument("\"123456\"")
                .add_argument("2")
                .add_argument("0")
                .expected_output("\"\"")
        )
        .add_example(Example::new().add_argument("50").add_argument("0").add_argument("10"))
        .add_example(
            Example::new().add_argument("\"123\"").add_argument("false").add_argument("10")
        )
        .add_example(
            Example::new().add_argument("\"123\"").add_argument("10").add_argument("{}")
        )
}