use crate::types::kam_toml::KamToml;

pub fn build_prop(kt: &KamToml) -> String {
    let mut content = String::new();
    content.push_str(&format!("id={}\n", kt.prop.id));
    content.push_str(&format!("name={}\n", kt.prop.name));
    content.push_str(&format!("version={}\n", kt.prop.version));
    content.push_str(&format!("versionCode={}\n", kt.prop.versionCode));
    content.push_str(&format!("author={}\n", kt.prop.author));
    content.push_str(&format!("description={}\n", kt.prop.description));
    if let Some(uj) = &kt.prop.updateJson {
        content.push_str(&format!("updateJson={}\n", uj));
    }
    content
}

pub fn build_module_json(kt: &KamToml) -> serde_json::Value {
    let metamodule = kt.prop.metamodule;
    let summary = kt.prop.description.clone();
    let source_url = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.repository.clone())
        .or_else(|| {
            kt.mmrl
                .as_ref()
                .and_then(|m| m.repo.as_ref())
                .and_then(|r| r.homepage.clone())
        })
        .unwrap_or_else(|| "".to_string());

    let mut authors: Vec<serde_json::Value> = vec![];
    if !kt.prop.author.trim().is_empty() {
        let mut author_obj = serde_json::Map::new();
        author_obj.insert(
            "type".to_string(),
            serde_json::Value::String("add".to_string()),
        );
        author_obj.insert(
            "name".to_string(),
            serde_json::Value::String(kt.prop.author.clone()),
        );
        authors.push(serde_json::Value::Object(author_obj));
    }
    if let Some(mmrl) = &kt.mmrl {
        if let Some(repo) = &mmrl.repo {
            if let Some(maintainers) = &repo.maintainers {
                for maint in maintainers {
                    match maint {
                        crate::types::kam_toml::sections::repo::MaintainerEntry::Name(s) => {
                            let mut obj = serde_json::Map::new();
                            obj.insert(
                                "type".to_string(),
                                serde_json::Value::String("add".to_string()),
                            );
                            obj.insert("name".to_string(), serde_json::Value::String(s.clone()));
                            authors.push(serde_json::Value::Object(obj));
                        }
                        crate::types::kam_toml::sections::repo::MaintainerEntry::Object(m) => {
                            let mut obj = serde_json::Map::new();
                            let t = m.r#type.clone().unwrap_or_else(|| "add".to_string());
                            obj.insert("type".to_string(), serde_json::Value::String(t));
                            obj.insert(
                                "name".to_string(),
                                serde_json::Value::String(m.name.clone()),
                            );
                            if let Some(link) = &m.link {
                                obj.insert(
                                    "link".to_string(),
                                    serde_json::Value::String(link.clone()),
                                );
                            }
                            authors.push(serde_json::Value::Object(obj));
                        }
                    }
                }
            }
        }
    }

    let mut root = serde_json::Map::new();
    root.insert(
        "metamodule".to_string(),
        serde_json::Value::Bool(metamodule),
    );
    root.insert("summary".to_string(), serde_json::Value::String(summary));
    root.insert(
        "sourceUrl".to_string(),
        serde_json::Value::String(source_url),
    );
    root.insert(
        "additionalAuthors".to_string(),
        serde_json::Value::Array(authors),
    );
    serde_json::Value::Object(root)
}

pub fn build_update_json(kt: &KamToml) -> serde_json::Value {
    let version = kt.prop.version.clone();
    let version_code = kt.prop.versionCode;

    let repo_url = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.repository.clone())
        .or_else(|| {
            kt.mmrl
                .as_ref()
                .and_then(|m| m.repo.as_ref())
                .and_then(|r| r.homepage.clone())
        })
        .unwrap_or_else(|| "".to_string());

    let zip_url = if repo_url.contains("github.com") {
        format!(
            "{}/releases/latest/download/{}.zip",
            repo_url.trim_end_matches('/'),
            kt.prop.id
        )
    } else if !repo_url.is_empty() {
        format!(
            "{}/releases/latest/download/{}.zip",
            repo_url.trim_end_matches('/'),
            kt.prop.id
        )
    } else {
        format!(
            "https://github.com/user/repo/releases/latest/download/{}.zip",
            kt.prop.id
        )
    };

    let changelog = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.changelog.clone())
        .or_else(|| {
            if !repo_url.is_empty() && repo_url.contains("github.com") {
                Some(format!(
                    "{}/raw/main/CHANGELOG.md",
                    repo_url.trim_end_matches('/')
                ))
            } else if !repo_url.is_empty() {
                Some(format!("{}/CHANGELOG.md", repo_url.trim_end_matches('/')))
            } else {
                Some("https://raw.githubusercontent.com/user/repo/main/CHANGELOG.md".to_string())
            }
        })
        .unwrap_or_else(|| "".to_string());

    let mut root = serde_json::Map::new();
    root.insert("version".to_string(), serde_json::Value::String(version));
    root.insert("versionCode".to_string(), serde_json::json!(version_code));
    root.insert("zipUrl".to_string(), serde_json::Value::String(zip_url));
    root.insert(
        "changelog".to_string(),
        serde_json::Value::String(changelog),
    );
    serde_json::Value::Object(root)
}

pub fn build_repo_json(kt: &KamToml) -> serde_json::Value {
    let mut root = serde_json::Map::new();

    if let Some(mmrl) = &kt.mmrl {
        if let Some(repo) = &mmrl.repo {
            if let Some(license) = &repo.license {
                if !license.is_empty() {
                    root.insert(
                        "license".to_string(),
                        serde_json::Value::String(license.clone()),
                    );
                }
            }

            if let Some(home) = &repo.homepage {
                if !home.is_empty() {
                    root.insert(
                        "homepage".to_string(),
                        serde_json::Value::String(home.clone()),
                    );
                }
            }

            if let Some(readme) = &repo.readme {
                if !readme.is_empty() {
                    root.insert(
                        "readme".to_string(),
                        serde_json::Value::String(readme.clone()),
                    );
                }
            }

            if let Some(screens) = &repo.screenshots {
                root.insert(
                    "screenshots".to_string(),
                    serde_json::Value::Array(
                        screens
                            .iter()
                            .map(|s| serde_json::Value::String(s.clone()))
                            .collect(),
                    ),
                );
            }

            if let Some(categories) = &repo.categories {
                root.insert(
                    "categories".to_string(),
                    serde_json::Value::Array(
                        categories
                            .iter()
                            .map(|s| serde_json::Value::String(s.clone()))
                            .collect(),
                    ),
                );
            }

            if let Some(devices) = &repo.devices {
                root.insert(
                    "devices".to_string(),
                    serde_json::Value::Array(
                        devices
                            .iter()
                            .map(|s| serde_json::Value::String(s.clone()))
                            .collect(),
                    ),
                );
            }

            if let Some(arch) = &repo.arch {
                root.insert(
                    "arch".to_string(),
                    serde_json::Value::Array(
                        arch.iter()
                            .map(|s| serde_json::Value::String(s.clone()))
                            .collect(),
                    ),
                );
            }

            if let Some(require) = &repo.require {
                root.insert(
                    "require".to_string(),
                    serde_json::Value::Array(
                        require
                            .iter()
                            .map(|s| serde_json::Value::String(s.clone()))
                            .collect(),
                    ),
                );
            }

            if let Some(donate) = &repo.donate {
                if !donate.is_empty() {
                    root.insert(
                        "donate".to_string(),
                        serde_json::Value::String(donate.clone()),
                    );
                }
            }

            if let Some(support) = &repo.support {
                if !support.is_empty() {
                    root.insert(
                        "support".to_string(),
                        serde_json::Value::String(support.clone()),
                    );
                }
            }

            if let Some(cover) = &repo.cover {
                if !cover.is_empty() {
                    root.insert(
                        "cover".to_string(),
                        serde_json::Value::String(cover.clone()),
                    );
                }
            }

            if let Some(icon) = &repo.icon {
                if !icon.is_empty() {
                    root.insert("icon".to_string(), serde_json::Value::String(icon.clone()));
                }
            }

            if let Some(maintainers) = &repo.maintainers {
                let mut arr: Vec<serde_json::Value> = vec![];
                for maint in maintainers {
                    match maint {
                        crate::types::kam_toml::sections::repo::MaintainerEntry::Name(s) => {
                            let mut obj = serde_json::Map::new();
                            obj.insert(
                                "type".to_string(),
                                serde_json::Value::String("add".to_string()),
                            );
                            obj.insert("name".to_string(), serde_json::Value::String(s.clone()));
                            arr.push(serde_json::Value::Object(obj));
                        }
                        crate::types::kam_toml::sections::repo::MaintainerEntry::Object(m) => {
                            let mut obj = serde_json::Map::new();
                            let t = m.r#type.clone().unwrap_or_else(|| "add".to_string());
                            obj.insert("type".to_string(), serde_json::Value::String(t));
                            obj.insert(
                                "name".to_string(),
                                serde_json::Value::String(m.name.clone()),
                            );
                            if let Some(link) = &m.link {
                                obj.insert(
                                    "link".to_string(),
                                    serde_json::Value::String(link.clone()),
                                );
                            }
                            arr.push(serde_json::Value::Object(obj));
                        }
                    }
                }
                root.insert("maintainers".to_string(), serde_json::Value::Array(arr));
            }

            if let Some(note) = &repo.note {
                if let Ok(v) = serde_json::to_value(note) {
                    root.insert("note".to_string(), v);
                }
            }

            if let Some(manager) = &repo.manager {
                if let Ok(v) = serde_json::to_value(manager) {
                    root.insert("manager".to_string(), v);
                }
            }
        }
    }
    serde_json::Value::Object(root)
}

pub fn build_track_json(kt: &KamToml) -> serde_json::Value {
    let mut root = serde_json::Map::new();

    root.insert(
        "id".to_string(),
        serde_json::Value::String(kt.prop.id.clone()),
    );
    root.insert("enable".to_string(), serde_json::Value::Bool(true));

    let source = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.repository.clone())
        .or_else(|| {
            kt.mmrl
                .as_ref()
                .and_then(|m| m.repo.as_ref())
                .and_then(|r| r.homepage.clone())
        })
        .or_else(|| kt.prop.updateJson.clone())
        .unwrap_or_else(|| "".to_string());
    root.insert("source".to_string(), serde_json::Value::String(source));

    let update_to = if let Some(uj) = &kt.prop.updateJson {
        uj.clone()
    } else if let Some(mmrl) = &kt.mmrl {
        if let Some(repo) = &mmrl.repo {
            if let Some(repo_url) = &repo.repository {
                format!(
                    "{}/releases/latest/download/{}.zip",
                    repo_url.trim_end_matches('/'),
                    kt.prop.id
                )
            } else {
                kt.prop.version.clone()
            }
        } else {
            kt.prop.version.clone()
        }
    } else {
        kt.prop.version.clone()
    };
    root.insert(
        "update_to".to_string(),
        serde_json::Value::String(update_to),
    );

    let repo_json = build_repo_json(&kt);
    if let Some(map) = repo_json.as_object() {
        for (k, v) in map {
            root.insert(k.clone(), v.clone());
        }
    }

    root.insert("enable".to_string(), serde_json::Value::Bool(true));

    let mut features_map = serde_json::Map::new();
    let keys = vec![
        "service",
        "post_fs_data",
        "resetprop",
        "sepolicy",
        "zygisk",
        "apks",
        "webroot",
        "post_mount",
        "boot_completed",
        "modconf",
    ];
    let repo_features = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.features.clone())
        .unwrap_or_default();
    for k in keys {
        let val = repo_features.iter().any(|s| s == k);
        features_map.insert(k.to_string(), serde_json::Value::Bool(val));
    }
    root.insert(
        "features".to_string(),
        serde_json::Value::Object(features_map),
    );

    serde_json::Value::Object(root)
}

pub fn build_config_json(kt: &KamToml) -> serde_json::Value {
    let mut root = serde_json::Map::new();

    let id = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.repository.as_ref())
        .map(|s| s.split('/').last().unwrap_or(s).to_string())
        .unwrap_or_else(|| kt.prop.id.clone());
    root.insert("id".to_string(), serde_json::Value::String(id));

    root.insert(
        "name".to_string(),
        serde_json::Value::String(kt.prop.name.clone()),
    );

    let base_url = kt
        .mmrl
        .as_ref()
        .and_then(|m| m.repo.as_ref())
        .and_then(|r| r.homepage.clone())
        .or_else(|| {
            kt.mmrl
                .as_ref()
                .and_then(|m| m.repo.as_ref().and_then(|r| r.repository.clone()))
        })
        .unwrap_or_else(|| String::new());
    let mut base_url = base_url.clone();
    if !base_url.is_empty() && !base_url.ends_with('/') {
        base_url.push('/');
    }
    root.insert("base_url".to_string(), serde_json::Value::String(base_url));

    if let Some(mmrl) = &kt.mmrl {
        if let Some(repo) = &mmrl.repo {
            if let Some(website) = &repo.homepage {
                if !website.is_empty() {
                    root.insert(
                        "website".to_string(),
                        serde_json::Value::String(website.clone()),
                    );
                }
            }
            if let Some(support) = &repo.support {
                if !support.is_empty() {
                    root.insert(
                        "support".to_string(),
                        serde_json::Value::String(support.clone()),
                    );
                }
            }
            if let Some(donate) = &repo.donate {
                if !donate.is_empty() {
                    root.insert(
                        "donate".to_string(),
                        serde_json::Value::String(donate.clone()),
                    );
                }
            }
            if let Some(sub) = &repo.documentation {
                if !sub.is_empty() {
                    root.insert(
                        "submission".to_string(),
                        serde_json::Value::String(sub.clone()),
                    );
                }
            }
        }
    }

    if !kt.prop.description.trim().is_empty() {
        root.insert(
            "description".to_string(),
            serde_json::Value::String(kt.prop.description.clone()),
        );
    }

    if let Some(mmrl) = &kt.mmrl {
        if let Some(repo) = &mmrl.repo {
            if let Some(max) = repo.max_num {
                root.insert("max_num".to_string(), serde_json::Value::Number(max.into()));
            }
        }
    }

    root.insert("enable_log".to_string(), serde_json::Value::Bool(true));
    root.insert(
        "log_dir".to_string(),
        serde_json::Value::String("log".to_string()),
    );

    serde_json::Value::Object(root)
}

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

    #[test]
    fn prop_basic() {
        let kt = KamToml::new_with_current_timestamp(
            "example_module_id".to_string(),
            "Example Module Name".to_string(),
            "1.0.0".to_string(),
            "Your Name".to_string(),
            "A description".to_string(),
            None,
            None,
        );
        let s = build_prop(&kt);
        assert!(s.contains("id=example_module_id"));
    }
}
