use crate::action::Action;
use json::{object, JsonValue};
use std::collections::BTreeMap;

#[derive(Clone)]
pub struct Swagger {
    openapi: String,
    info: Info,
    servers: Vec<Server>,
    /// 组件 密钥认证设置
    components: JsonValue,
    /// 分类标签
    tags: BTreeMap<String, Tag>,

    security: Vec<JsonValue>,
    /// 路径
    paths: BTreeMap<String, BTreeMap<String, Api>>,
}

impl Swagger {
    #[must_use] pub fn new(version: &str, title: &str, description: &str) -> Swagger {
        Swagger {
            openapi: "3.0.0".to_string(),
            info: Info {
                title: title.to_string(),
                description: description.to_string(),
                version: version.to_string(),
            },
            servers: vec![],
            components: object! {},
            tags: BTreeMap::default(),
            security: vec![],
            paths: BTreeMap::default(),
        }
    }
    pub fn add_server(&mut self, url: &str, description: &str) -> Server {
        Server {
            url: url.to_string(),
            description: description.to_string(),
            variables: BTreeMap::default(),
        }
    }
    pub fn set_server(&mut self, server: Server) {
        self.servers.push(server);
    }
    pub fn add_header(&mut self, key: &str, description: &str, example: &str) {
        self.components["parameters"]["GlobalHeader"] = object! {
            "name":key,
            "in": "header",
            "description": description,
            "required": true,
            "schema": {
                "type": "string",
                "example":example
            }
        }
    }
    pub fn add_components_bearer_token(&mut self) {
        self.components["securitySchemes"]["BearerToken"] = object! {
            "type": "http",
            "scheme": "bearer",
            "bearerFormat": "Token"
        };
        self.security.push(object! {
            "BearerToken":[]
        });
    }
    pub fn add_components_header(&mut self, key: &str, description: &str, example: &str) {
        self.components["securitySchemes"][key] = object! {
            "type": "apiKey",
            "in": "header",
            "name": key,
            "description": description,
        };
        let mut security = object! {};
        security[key] = example.into();
        self.security.push(security);
    }
    pub fn add_authorization_header(&mut self, token: &str) {
        self.components["parameters"]["AuthorizationHeader"] = object! {
            "name": "Authorization",
            "in": "header",
            "required": true,
            "description": "Bearer token for authentication",
            "schema":{
                "type": "string",
                "example":format!("Bearer {}",token)
            }
        };
    }

    pub fn set_global(&mut self, key: &str, example: &str, description: &str) {
        self.components["schemas"][key]["type"] = "string".into();
        self.components["schemas"][key]["description"] = description.into();
        self.components["schemas"][key]["example"] = example.into();
    }

    pub fn add_tags(&mut self, name: &str, description: &str) {
        self.tags.insert(
            name.to_string(),
            Tag {
                name: name.to_string(),
                description: description.to_string(),
            },
        );
    }
    pub fn add_paths(&mut self, mut action: Box<dyn Action>) {
        let path = format!("/{}", action.api().replace('.', "/"));
        let mut t = BTreeMap::new();
        t.insert(
            action.method().str().to_lowercase().clone(),
            Api::new(action, &self.components.clone()),
        );
        self.paths.insert(path, t);
    }
    pub fn add_path(&mut self, mut action: Box<dyn Action>) {
        let path = format!("/{}", action.path());
        if self.paths.contains_key(&path) {
            let mut t=self.paths.get(&path).unwrap().clone();
            t.insert(
                action.method().str().to_lowercase().clone(),
                Api::new(action, &self.components.clone()),
            );
            self.paths.insert(path, t);
        } else {
            let mut t = BTreeMap::new();
            t.insert(
                action.method().str().to_lowercase().clone(),
                Api::new(action, &self.components.clone()),
            );
            self.paths.insert(path, t);
        }
    }
    pub fn add_tag_paths(&mut self, tag: &str, mut action: Box<dyn Action>) {
        let path = format!("/{tag}/{}", action.api().replace('.', "/"));
        let mut t = BTreeMap::new();
        t.insert(
            action.method().str().to_lowercase().clone(),
            Api::new_tag(tag, action, &self.components.clone()),
        );
        self.paths.insert(path, t);
    }
    pub fn json(&mut self) -> JsonValue {
        let mut paths = BTreeMap::new();
        for (key, value) in &mut self.paths {
            let mut t = BTreeMap::new();
            for (x, y) in value.iter_mut() {
                t.insert(x.clone(), y.json().clone());
            }
            paths.insert(key.clone(), t.clone());
        }
        object! {
            openapi: self.openapi.clone(),
            info: self.info.json(),
            servers: self.servers.iter().map(Server::json).collect::<Vec<JsonValue>>().clone(),
            components: self.components.clone(),
            security:self.security.clone(),
            tags: self.tags.values().map(Tag::json).collect::<Vec<JsonValue>>().clone(),
            paths: paths.clone()
        }
    }
}
/// 接口项目信息
#[derive(Clone)]
struct Info {
    /// 项目标题
    title: String,
    /// 项目描述
    description: String,
    /// 项目版本
    version: String,
}
impl Info {
    pub fn json(&self) -> JsonValue {
        object! {
            title: self.title.clone(),
            description: self.description.clone(),
            version: self.version.clone()
        }
    }
}
/// 服务器地址
#[derive(Clone, Debug)]
pub struct Server {
    url: String,
    description: String,
    variables: BTreeMap<String, JsonValue>,
}
impl Server {
    #[must_use] pub fn json(&self) -> JsonValue {
        object! {
            url: self.url.clone(),
            description: self.description.clone(),
            variables: self.variables.clone()
        }
    }
    pub fn set_variable(&mut self, key: &str, value: JsonValue, description: &str) {
        self.variables.insert(
            key.to_string(),
            object! {
                default:value,
                description:description
            },
        );
    }
}
/// 标签分类
#[derive(Clone)]
struct Tag {
    /// 标签名称
    name: String,
    /// 标签描述
    description: String,
}
impl Tag {
    pub fn json(&self) -> JsonValue {
        object! {
            name: self.name.clone(),
            description: self.description.clone(),
        }
    }
}
#[derive(Clone)]
struct Api {
    tags: Vec<String>,
    summary: String,
    // parameters: JsonValue,
    description: String,
    /// 请求消息体
    request_body: RequestBody,
    /// 响应消息体
    responses: JsonValue,
}

impl Api {
    pub fn new_tag(tag: &str, mut action: Box<dyn Action>, components: &JsonValue) -> Api {
        let apis = action.api();
        let binding = apis.clone();
        let mut apis = binding.split('.');
        let mut t = Self {
            tags: vec![format!(
                "{tag}.{}.{}",
                apis.next().unwrap().to_string(),
                apis.next().unwrap().to_string()
            )],
            summary: action.title().to_string(),
            description: action.description().to_string(),
            request_body: RequestBody::new(components.clone()),
            responses: object! {},
            // parameters: object! {},
        };

        if !action.params().is_empty() {
            t.request_body.set_required(true);
            t.request_body
                .set_content(action.content_type().str().as_str(), &action.params());
        }
        t
    }
    pub fn new(mut action: Box<dyn Action>, components: &JsonValue) -> Api {
        let apis = action.api();
        let binding = apis.clone();
        let mut apis = binding.split('.');
        let mut t = Self {
            tags: vec![format!(
                "{}.{}",
                apis.next().unwrap().to_string(),
                apis.next().unwrap().to_string()
            )],
            summary: action.title().to_string(),
            description: action.description().to_string(),
            request_body: RequestBody::new(components.clone()),
            responses: object! {},
            // parameters: object! {},
        };

        if !action.params().is_empty() {
            t.request_body.set_required(true);
            t.request_body
                .set_content(action.content_type().str().as_str(), &action.params());
        }
        t
    }
    pub fn json(&self) -> JsonValue {
        let mut t = object! {
            tags:self.tags.clone(),
            summary: self.summary.clone(),
            description: self.description.clone(),
            requestBody: self.request_body.json(),
            responses: self.responses.clone(),
        };
        if !t["requestBody"]["required"].as_bool().unwrap() {
            t.remove("requestBody");
        }
        t
    }
}

#[derive(Clone)]
struct RequestBody {
    required: bool,
    content: JsonValue,
    components: JsonValue,
}
impl RequestBody {
    pub fn new(components: JsonValue) -> RequestBody {
        Self {
            required: false,
            content: object! {},
            components,
        }
    }
    pub fn set_required(&mut self, state: bool) {
        self.required = state;
    }
    pub fn set_content(&mut self, content_type: &str, params: &JsonValue) {
        self.content[content_type] = object! {
            schema:object! {"type":if params.is_array() {"array"}else{"object"}}
        };
        match self.content[content_type]["schema"]["type"]
            .as_str()
            .unwrap_or("")
        {
            "object" => self
                .clone()
                .set_schema_object(&mut self.content[content_type]["schema"], &params.clone()),
            "array" => RequestBody::set_schema_array(
                &mut self.content[content_type]["schema"],
                params.clone(),
            ),
            _ => {}
        }
        for (field, data) in params.entries() {
            if self.clone().components["schemas"][field].is_empty() {
                self.content[content_type]["example"][field] = data["example"].clone();
            } else {
                self.content[content_type]["example"][field] =
                    self.clone().components["schemas"][field]["example"].clone();
            }
        }
    }
    fn set_schema_object(self, data: &mut JsonValue, params: &JsonValue) {
        for (key, value) in params.entries() {
            data["properties"][key]["type"] =
                RequestBody::mode(value["mode"].as_str().unwrap_or(""));
            match value["mode"].as_str().unwrap_or("") {
                "radio" | "select" => {
                    data["properties"][key]["enum"] = value["option"].clone();
                }
                _ => {}
            }
            match data["properties"][key]["type"].as_str().unwrap_or("") {
                "object" => self
                    .clone()
                    .set_sub_object(&mut data["properties"][key], &value["items"].clone()),
                "array" => {
                    data["properties"][key]["example"] = value["example"].clone();
                    data["properties"][key]["default"] = value["example"].clone();
                    RequestBody::set_sub_array(&mut data["properties"][key], &value["items"].clone());
                }
                _ => {
                    RequestBody::set_schema_str(&mut data["properties"][key], &value.clone());
                }
            }
        }
    }
    // fn set_schema_components(data: &mut JsonValue, key: &str, _value: JsonValue) {
    //     data["$ref"] = format!("#/components/schemas/{}", key).into();
    // }
    fn set_schema_str(data: &mut JsonValue, params: &JsonValue) {
        data["type"] = RequestBody::mode(params["mode"].as_str().unwrap_or(""));
        data["example"] = params["example"].clone();
        data["default"] = params["example"].clone();
    }
    fn set_schema_array(data: &mut JsonValue, params: JsonValue) {
        data["items"] = params;
    }
    fn set_sub_array(data: &mut JsonValue, params: &JsonValue) {
        data["items"]["type"] = params["mode"].as_str().unwrap_or("string").into();
    }
    fn set_sub_object(self, data: &mut JsonValue, params: &JsonValue) {
        for (key, value) in params.entries() {
            data["properties"][key]["type"] =
                RequestBody::mode(value["mode"].as_str().unwrap_or(""));
            match value["mode"].as_str().unwrap_or("") {
                "radio" | "select" => {
                    data["properties"][key]["enum"] = value["option"].clone();
                }
                _ => {}
            }
            match data["properties"][key]["type"].as_str().unwrap_or("") {
                "object" => self
                    .clone()
                    .set_sub_object(&mut data["properties"][key], &value["items"].clone()),
                "array" => {
                    RequestBody::set_sub_array(&mut data["properties"][key], &value["items"].clone());
                }
                _ => {
                    RequestBody::set_schema_str(&mut data["properties"][key], &value.clone());
                }
            }
        }
    }
    fn mode(name: &str) -> JsonValue {
        match name {
            "int" => "integer",
            "switch" => "boolean",
            "radio" => "string",
            "select" | "array" => "array",
            _ => name,
        }
        .into()
    }
    pub fn json(&self) -> JsonValue {
        object! {
                required: self.required,
                content: self.content.clone()
        }
    }
}
