use big_s::S;

use super::*;

#[test]
fn test_contained_in() {
    assert!(contained_in("animaux", "animaux"));
    assert!(contained_in("animaux.chien", "animaux"));
    assert!(contained_in("animaux.chien.race.bouvier bernois.fourrure.couleur", "animaux"));
    assert!(contained_in("animaux.chien.race.bouvier bernois.fourrure.couleur", "animaux.chien"));
    assert!(contained_in(
        "animaux.chien.race.bouvier bernois.fourrure.couleur",
        "animaux.chien.race.bouvier bernois"
    ));
    assert!(contained_in(
        "animaux.chien.race.bouvier bernois.fourrure.couleur",
        "animaux.chien.race.bouvier bernois.fourrure"
    ));
    assert!(contained_in(
        "animaux.chien.race.bouvier bernois.fourrure.couleur",
        "animaux.chien.race.bouvier bernois.fourrure.couleur"
    ));

    // -- the wrongs
    assert!(!contained_in("chien", "chat"));
    assert!(!contained_in("animaux", "animaux.chien"));
    assert!(!contained_in("animaux.chien", "animaux.chat"));

    // -- the strange edge cases
    assert!(!contained_in("animaux.chien", "anima"));
    assert!(!contained_in("animaux.chien", "animau"));
    assert!(!contained_in("animaux.chien", "animaux."));
    assert!(!contained_in("animaux.chien", "animaux.c"));
    assert!(!contained_in("animaux.chien", "animaux.ch"));
    assert!(!contained_in("animaux.chien", "animaux.chi"));
    assert!(!contained_in("animaux.chien", "animaux.chie"));
}

#[test]
fn simple_key() {
    let value: Value = json!({
        "name": "peanut",
        "age": 8,
        "race": {
            "name": "bernese mountain",
            "avg_age": 12,
            "size": "80cm",
        }
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["name"]).into();
    assert_eq!(
        res,
        json!({
            "name": "peanut",
        })
    );

    let res: Value = select_values(value.clone(), vec!["age"]).into();
    assert_eq!(
        res,
        json!({
            "age": 8,
        })
    );

    let res: Value = select_values(value.clone(), vec!["name", "age"]).into();
    assert_eq!(
        res,
        json!({
            "name": "peanut",
            "age": 8,
        })
    );

    let res: Value = select_values(value.clone(), vec!["race"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["name", "age", "race"]).into();
    assert_eq!(
        res,
        json!({
            "name": "peanut",
            "age": 8,
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );
}

#[test]
fn complex_key() {
    let value: Value = json!({
        "name": "peanut",
        "age": 8,
        "race": {
            "name": "bernese mountain",
            "avg_age": 12,
            "size": "80cm",
        }
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["race"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );

    println!("RIGHT BEFORE");

    let res: Value = select_values(value.clone(), vec!["race.name"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["race.name", "race.size"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
                "size": "80cm",
            }
        })
    );

    let res: Value = select_values(
        value.clone(),
        vec!["race.name", "race.size", "race.avg_age", "race.size", "age"],
    )
    .into();
    assert_eq!(
        res,
        json!({
            "age": 8,
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["race.name", "race"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["race", "race.name"]).into();
    assert_eq!(
        res,
        json!({
            "race": {
                "name": "bernese mountain",
                "avg_age": 12,
                "size": "80cm",
            }
        })
    );
}

#[test]
fn multi_level_nested() {
    let value: Value = json!({
        "jean": {
            "age": 8,
            "race": {
                "name": "bernese mountain",
                "size": "80cm",
            }
        }
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["jean"]).into();
    assert_eq!(
        res,
        json!({
            "jean": {
                "age": 8,
                "race": {
                    "name": "bernese mountain",
                    "size": "80cm",
                }
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["jean.age"]).into();
    assert_eq!(
        res,
        json!({
            "jean": {
                "age": 8,
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["jean.race.size"]).into();
    assert_eq!(
        res,
        json!({
            "jean": {
                "race": {
                    "size": "80cm",
                }
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["jean.race.name", "jean.age"]).into();
    assert_eq!(
        res,
        json!({
            "jean": {
                "age": 8,
                "race": {
                    "name": "bernese mountain",
                }
            }
        })
    );

    let res: Value = select_values(value.clone(), vec!["jean.race"]).into();
    assert_eq!(
        res,
        json!({
            "jean": {
                "race": {
                    "name": "bernese mountain",
                    "size": "80cm",
                }
            }
        })
    );
}

#[test]
fn array_and_deep_nested() {
    let value: Value = json!({
        "doggos": [
            {
                "jean": {
                    "age": 8,
                    "race": {
                        "name": "bernese mountain",
                        "size": "80cm",
                    }
                }
            },
            {
                "marc": {
                    "age": 4,
                    "race": {
                        "name": "golden retriever",
                        "size": "60cm",
                    }
                }
            },
        ]
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["doggos.jean"]).into();
    assert_eq!(
        res,
        json!({
            "doggos": [
                {
                    "jean": {
                        "age": 8,
                        "race": {
                            "name": "bernese mountain",
                            "size": "80cm",
                        }
                    }
                }
            ]
        })
    );

    let res: Value = select_values(value.clone(), vec!["doggos.marc"]).into();
    assert_eq!(
        res,
        json!({
            "doggos": [
                {
                    "marc": {
                        "age": 4,
                        "race": {
                            "name": "golden retriever",
                            "size": "60cm",
                        }
                    }
                }
            ]
        })
    );

    let res: Value = select_values(value.clone(), vec!["doggos.marc.race"]).into();
    assert_eq!(
        res,
        json!({
            "doggos": [
                {
                    "marc": {
                        "race": {
                            "name": "golden retriever",
                            "size": "60cm",
                        }
                    }
                }
            ]
        })
    );

    let res: Value =
        select_values(value.clone(), vec!["doggos.marc.race.name", "doggos.marc.age"]).into();

    assert_eq!(
        res,
        json!({
            "doggos": [
                {
                    "marc": {
                        "age": 4,
                        "race": {
                            "name": "golden retriever",
                        }
                    }
                }
            ]
        })
    );

    let res: Value = select_values(
        value.clone(),
        vec!["doggos.marc.race.name", "doggos.marc.age", "doggos.jean.race.name", "other.field"],
    )
    .into();

    assert_eq!(
        res,
        json!({
            "doggos": [
                {
                    "jean": {
                        "race": {
                            "name": "bernese mountain",
                        }
                    }
                },
                {
                    "marc": {
                        "age": 4,
                        "race": {
                            "name": "golden retriever",
                        }
                    }
                }
            ]
        })
    );
}

#[test]
fn empty_array_object_return_empty() {
    let value: Value = json!({
        "array": [],
        "object": {},
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["array.name", "object.name"]).into();
    assert_eq!(
        res,
        json!({
            "array": [],
            "object": {},
        })
    );
}

#[test]
fn all_conflict_variation() {
    let value: Value = json!({
       "pet.dog.name": "jean",
       "pet.dog": {
         "name": "bob"
       },
       "pet": {
         "dog.name": "michel"
       },
       "pet": {
         "dog": {
           "name": "milan"
         }
       }
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["pet.dog.name"]).into();
    assert_eq!(
        res,
        json!({
           "pet.dog.name": "jean",
           "pet.dog": {
             "name": "bob"
           },
           "pet": {
             "dog.name": "michel"
           },
           "pet": {
             "dog": {
               "name": "milan"
             }
           }
        })
    );

    let value: Value = json!({
       "pet.dog.name": "jean",
       "pet.dog": {
         "name": "bob",
       },
       "pet": {
         "dog.name": "michel",
         "dog": {
           "name": "milan",
         }
       }
    });
    let value: &Document = value.as_object().unwrap();

    let res: Value = select_values(value.clone(), vec!["pet.dog.name", "pet.dog", "pet"]).into();

    assert_eq!(
        res,
        json!({
           "pet.dog.name": "jean",
           "pet.dog": {
             "name": "bob",
           },
           "pet": {
             "dog.name": "michel",
             "dog": {
               "name": "milan",
             }
           }
        })
    );
}

#[test]
fn map_object() {
    let mut value: Value = json!({
        "jean": {
            "age": 8,
            "race": {
                "name": "bernese mountain",
                "size": "80cm",
            }
        }
    });

    map_leaf_values(value.as_object_mut().unwrap(), ["jean.race.name"], |key, _, value| {
        match (value, key) {
            (Value::String(name), "jean.race.name") => *name = S("patou"),
            _ => unreachable!(),
        }
    });

    assert_eq!(
        value,
        json!({
            "jean": {
                "age": 8,
                "race": {
                    "name": "patou",
                    "size": "80cm",
                }
            }
        })
    );

    let mut value: Value = json!({
        "jean": {
            "age": 8,
            "race": {
                "name": "bernese mountain",
                "size": "80cm",
            }
        },
        "bob": "lolpied",
    });

    let mut calls = 0;
    map_leaf_values(value.as_object_mut().unwrap(), ["jean"], |key, _, value| {
        calls += 1;
        match (value, key) {
            (Value::String(name), "jean.race.name") => *name = S("patou"),
            _ => println!("Called with {key}"),
        }
    });

    assert_eq!(calls, 3);
    assert_eq!(
        value,
        json!({
            "jean": {
                "age": 8,
                "race": {
                    "name": "patou",
                    "size": "80cm",
                }
            },
            "bob": "lolpied",
        })
    );
}

#[test]
fn map_array() {
    let mut value: Value = json!({
        "no_array": "peter",
        "simple": ["foo", "bar"],
        "nested": [
            {
                "a": [
                    ["cat", "dog"],
                    ["fox", "bear"],
                ],
                "b": "hi",
            },
            {
                "a": ["green", "blue"],
            },
        ],
    });

    map_leaf_values(
        value.as_object_mut().unwrap(),
        ["no_array", "simple", "nested"],
        |_key, array_indices, value| {
            *value = format!("{array_indices:?}").into();
        },
    );

    assert_eq!(
        value,
        json!({
            "no_array": "[]",
            "simple": ["[0]", "[1]"],
            "nested": [
                {
                    "a": [
                        ["[0, 0, 0]", "[0, 0, 1]"],
                        ["[0, 1, 0]", "[0, 1, 1]"],
                    ],
                    "b": "[0]",
                },
                {
                    "a": ["[1, 0]", "[1, 1]"],
                },
            ],
        })
    );
}
