use cow_utils::CowUtils;
use serde_json::{Map, Value};

pub fn merge_metadata(parent: &Value, child: &Value) -> Value {
    let parent_obj = parent.as_object();
    let child_obj = match child.as_object() {
        Some(obj) => obj,
        None => return parent.clone(),
    };

    let mut merged = parent_obj.cloned().unwrap_or_default();

    if let Some(child_title) = child_obj.get("title") {
        if let Some(child_title_str) = child_title.as_str() {
            if let Some(parent_title) = parent_obj.and_then(|p| p.get("title")) {
                if let Some(parent_title_obj) = parent_title.as_object() {
                    if let Some(template) =
                        parent_title_obj.get("template").and_then(|t| t.as_str())
                    {
                        let replaced = template.cow_replace("%s", child_title_str).into_owned();
                        merged.insert("title".to_string(), Value::String(replaced));
                    } else {
                        merged.insert("title".to_string(), child_title.clone());
                    }
                } else {
                    merged.insert("title".to_string(), child_title.clone());
                }
            } else {
                merged.insert("title".to_string(), child_title.clone());
            }
        } else {
            merged.insert("title".to_string(), child_title.clone());
        }
    }

    merge_simple_field(&mut merged, child_obj, "description");
    merge_simple_field(&mut merged, child_obj, "keywords");
    merge_simple_field(&mut merged, child_obj, "manifest");
    merge_simple_field(&mut merged, child_obj, "themeColor");
    merge_simple_field(&mut merged, child_obj, "viewport");
    merge_simple_field(&mut merged, child_obj, "canonical");

    merge_object_field(&mut merged, parent_obj, child_obj, "openGraph");
    merge_object_field(&mut merged, parent_obj, child_obj, "twitter");
    merge_object_field(&mut merged, parent_obj, child_obj, "robots");
    merge_object_field(&mut merged, parent_obj, child_obj, "icons");
    merge_object_field(&mut merged, parent_obj, child_obj, "appleWebApp");

    Value::Object(merged)
}

fn merge_simple_field(merged: &mut Map<String, Value>, child: &Map<String, Value>, field: &str) {
    if let Some(value) = child.get(field) {
        merged.insert(field.to_string(), value.clone());
    }
}

fn merge_object_field(
    merged: &mut Map<String, Value>,
    parent: Option<&Map<String, Value>>,
    child: &Map<String, Value>,
    field: &str,
) {
    if let Some(child_value) = child.get(field)
        && let Some(child_obj) = child_value.as_object()
    {
        let mut result = Map::new();

        if let Some(parent_value) = parent.and_then(|p| p.get(field))
            && let Some(parent_obj) = parent_value.as_object()
        {
            for (k, v) in parent_obj {
                result.insert(k.clone(), v.clone());
            }
        }

        for (k, v) in child_obj {
            result.insert(k.clone(), v.clone());
        }

        merged.insert(field.to_string(), Value::Object(result));
    }
}

pub fn finalize_metadata(metadata: &mut Value) {
    if let Some(obj) = metadata.as_object_mut()
        && let Some(title) = obj.get("title")
        && let Some(title_obj) = title.as_object()
    {
        if let Some(absolute) = title_obj.get("absolute") {
            obj.insert("title".to_string(), absolute.clone());
        } else if let Some(default) = title_obj.get("default") {
            obj.insert("title".to_string(), default.clone());
        } else if let Some(template) = title_obj.get("template") {
            obj.insert("title".to_string(), template.clone());
        } else {
            obj.insert("title".to_string(), Value::String(String::new()));
        }
    }
}

#[cfg(test)]
#[allow(clippy::disallowed_methods)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_merge_simple_fields() {
        let parent = json!({
            "title": "Parent Title",
            "description": "Parent Description"
        });
        let child = json!({
            "description": "Child Description",
            "keywords": ["test"]
        });

        let result = merge_metadata(&parent, &child);

        assert_eq!(result["title"], "Parent Title");
        assert_eq!(result["description"], "Child Description");
        assert_eq!(result["keywords"], json!(["test"]));
    }

    #[test]
    fn test_merge_title_with_template() {
        let parent = json!({
            "title": {
                "template": "%s | My Site"
            }
        });
        let child = json!({
            "title": "Page Title"
        });

        let result = merge_metadata(&parent, &child);

        assert_eq!(result["title"], "Page Title | My Site");
    }

    #[test]
    fn test_merge_object_fields() {
        let parent = json!({
            "openGraph": {
                "siteName": "My Site",
                "type": "website"
            }
        });
        let child = json!({
            "openGraph": {
                "title": "Page Title",
                "type": "article"
            }
        });

        let result = merge_metadata(&parent, &child);

        let og = result["openGraph"].as_object().unwrap();
        assert_eq!(og["siteName"], "My Site");
        assert_eq!(og["title"], "Page Title");
        assert_eq!(og["type"], "article");
    }

    #[test]
    fn test_finalize_metadata_absolute() {
        let mut metadata = json!({
            "title": {
                "absolute": "Absolute Title",
                "default": "Default Title"
            }
        });

        finalize_metadata(&mut metadata);

        assert_eq!(metadata["title"], "Absolute Title");
    }

    #[test]
    fn test_finalize_metadata_default() {
        let mut metadata = json!({
            "title": {
                "default": "Default Title"
            }
        });

        finalize_metadata(&mut metadata);

        assert_eq!(metadata["title"], "Default Title");
    }

    #[test]
    fn test_finalize_metadata_string() {
        let mut metadata = json!({
            "title": "String Title"
        });

        finalize_metadata(&mut metadata);

        assert_eq!(metadata["title"], "String Title");
    }

    #[test]
    fn test_finalize_metadata_template_only() {
        let mut metadata = json!({
            "title": {
                "template": "%s | My Site"
            }
        });

        finalize_metadata(&mut metadata);

        assert_eq!(metadata["title"], "%s | My Site");
    }

    #[test]
    fn test_finalize_metadata_empty_object() {
        let mut metadata = json!({
            "title": {}
        });

        finalize_metadata(&mut metadata);

        assert_eq!(metadata["title"], "");
    }

    #[test]
    fn test_merge_metadata_child_not_object() {
        let parent = json!({
            "title": "Parent Title",
            "description": "Parent Description"
        });
        let child = json!("not an object");

        let result = merge_metadata(&parent, &child);

        assert_eq!(result["title"], "Parent Title");
        assert_eq!(result["description"], "Parent Description");
    }

    #[test]
    fn test_merge_metadata_parent_not_object() {
        let parent = json!("not an object");
        let child = json!({
            "title": "Child Title",
            "description": "Child Description"
        });

        let result = merge_metadata(&parent, &child);

        assert_eq!(result["title"], "Child Title");
        assert_eq!(result["description"], "Child Description");
    }
}
