use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InitializeParams {
    #[serde(rename = "processId")]
    pub process_id: i32,
    #[serde(rename = "clientInfo", skip_serializing_if = "Option::is_none")]
    pub client_info: Option<ClientInfo>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub locale: Option<String>,
    #[serde(rename = "rootPath", skip_serializing_if = "Option::is_none")]
    pub root_path: Option<String>,
    // Can be null but needs to be printed always.
    #[serde(rename = "rootUri")]
    pub root_uri: Option<String>,
    #[serde(
        rename = "initializationOptions",
        skip_serializing_if = "Option::is_none"
    )]
    pub initialization_options: Option<Value>,
    pub capabilities: ClientCapabilities,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trace: Option<String>,
    #[serde(rename = "workspaceFolders", skip_serializing_if = "Option::is_none")]
    pub workspace_folders: Option<Vec<WorkspaceFolder>>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ClientInfo {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ClientCapabilities {
    #[serde(rename = "textDocument", skip_serializing_if = "Option::is_none")]
    pub text_document: Option<TextDocumentClientCapabilities>,
    #[serde(rename = "notebookDocument", skip_serializing_if = "Option::is_none")]
    pub notebook_document: Option<NotebookDocumentClientCapabilities>,
    pub workspace: Option<ClientCapabilitiesWorkspace>,
    pub window: Option<Window>,
    pub general: Option<General>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub experimental: Option<Value>,
}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TextDocumentClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct NotebookDocumentClientCapabilities {}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ClientCapabilitiesWorkspace {
    #[serde(rename = "applyEdit", skip_serializing_if = "Option::is_none")]
    pub apply_edit: Option<bool>,
    #[serde(rename = "workspaceEdit", skip_serializing_if = "Option::is_none")]
    pub workspace_edit: Option<WorkspaceEditClientCapabilities>,
    #[serde(
        rename = "didChangeConfiguration",
        skip_serializing_if = "Option::is_none"
    )]
    pub did_change_configuration: Option<DidChangeConfigurationClientCapabilities>,
    #[serde(
        rename = "didChangeWatchedFiles",
        skip_serializing_if = "Option::is_none"
    )]
    pub did_change_watched_files: Option<DidChangeWatchedFilesClientCapabilities>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub symbol: Option<WorkspaceSymbolClientCapabilities>,
    #[serde(rename = "executeCommand", skip_serializing_if = "Option::is_none")]
    pub execute_command: Option<ExecuteCommandClientCapabilities>,
    #[serde(rename = "workspaceFolders", skip_serializing_if = "Option::is_none")]
    pub workspace_folders: Option<bool>,
    pub configuration: Option<bool>,
    #[serde(rename = "semanticTokens", skip_serializing_if = "Option::is_none")]
    pub semantic_tokens: Option<SemanticTokensWorkspaceClientCapabilities>,
    #[serde(rename = "codeLens", skip_serializing_if = "Option::is_none")]
    pub code_lens: Option<CodeLensWorkspaceClientCapabilities>,
    #[serde(rename = "fileOperations", skip_serializing_if = "Option::is_none")]
    pub file_operations: Option<FileOperations>,
    #[serde(rename = "inlineValue", skip_serializing_if = "Option::is_none")]
    pub inline_value: Option<InlineValueWorkspaceClientCapabilities>,
    #[serde(rename = "inlayHint", skip_serializing_if = "Option::is_none")]
    pub inlay_hint: Option<InlayHintWorkspaceClientCapabilities>,
    pub diagnostics: Option<DiagnosticWorkspaceClientCapabilities>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct FileOperations {
    #[serde(
        rename = "dynamicRegistration",
        skip_serializing_if = "Option::is_none"
    )]
    pub dynamic_registration: Option<bool>,
    #[serde(rename = "didCreate", skip_serializing_if = "Option::is_none")]
    pub did_create: Option<bool>,
    #[serde(rename = "willCreate", skip_serializing_if = "Option::is_none")]
    pub will_create: Option<bool>,
    #[serde(rename = "didRename", skip_serializing_if = "Option::is_none")]
    pub did_rename: Option<bool>,
    #[serde(rename = "willRename", skip_serializing_if = "Option::is_none")]
    pub will_rename: Option<bool>,
    #[serde(rename = "didDelete", skip_serializing_if = "Option::is_none")]
    pub did_delete: Option<bool>,
    #[serde(rename = "willDelete", skip_serializing_if = "Option::is_none")]
    pub will_delete: Option<bool>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Window {
    #[serde(rename = "workDoneProgress", skip_serializing_if = "Option::is_none")]
    pub work_done_progress: Option<bool>,
    #[serde(rename = "showMessage", skip_serializing_if = "Option::is_none")]
    pub show_message: Option<ShowMessageRequestClientCapabilities>,
    #[serde(rename = "showDocument", skip_serializing_if = "Option::is_none")]
    pub show_document: Option<ShowDocumentClientCapabilities>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct General {
    #[serde(
        rename = "staleRequestSupport",
        skip_serializing_if = "Option::is_none"
    )]
    pub stale_request_support: Option<StaleRequestSupport>,
    #[serde(rename = "regularExpressions", skip_serializing_if = "Option::is_none")]
    pub regular_expressions: Option<RegularExpressionsClientCapabilities>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub markdown: Option<MarkdownClientCapabilities>,
    #[serde(rename = "positionEncodings", skip_serializing_if = "Option::is_none")]
    pub position_encodings: Option<Vec<String>>,
}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceEditClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DidChangeConfigurationClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DidChangeWatchedFilesClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceSymbolClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ExecuteCommandClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SemanticTokensWorkspaceClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InlineValueWorkspaceClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CodeLensWorkspaceClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InlayHintWorkspaceClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DiagnosticWorkspaceClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ShowMessageRequestClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ShowDocumentClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RegularExpressionsClientCapabilities {}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct MarkdownClientCapabilities {}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaleRequestSupport {
    cancel: bool,
    #[serde(rename = "retryOnContentModified")]
    retry_on_content_modified: Option<Vec<String>>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WorkspaceFolder {
    uri: String,
    name: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct InitializedResult {
    pub capabilities: ServerCapabilities,
    #[serde(rename = "serverInfo", skip_serializing_if = "Option::is_none")]
    pub server_info: Option<ServerInfo>,
}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ServerCapabilities {
    #[serde(rename = "positionEncoding", skip_serializing_if = "Option::is_none")]
    pub position_encoding: Option<String>,
    #[serde(rename = "completionProvider", skip_serializing_if = "Option::is_none")]
    pub completion_provider: Option<CompletionOptions>,
    #[serde(rename = "textDocumentSync")]
    pub text_document_sync: Option<TextDocumentSyncOptions>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TextDocumentSyncOptions {
    #[serde(rename = "openClose", skip_serializing_if = "Option::is_none")]
    pub open_close: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub change: Option<TextDocumentSyncKind>,
}

#[repr(i32)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum TextDocumentSyncKind {
    None = 0,
    Full = 1,
    Incremental = 2,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompletionOptions {
    #[serde(rename = "triggerCharacters", skip_serializing_if = "Option::is_none")]
    pub trigger_characters: Option<Vec<String>>,
    #[serde(
        rename = "allCommitCharacters",
        skip_serializing_if = "Option::is_none"
    )]
    pub all_commit_characters: Option<Vec<String>>,
    #[serde(rename = "resolveProvider", skip_serializing_if = "Option::is_none")]
    pub resolve_provider: Option<bool>,
    #[serde(rename = "completionItem", skip_serializing_if = "Option::is_none")]
    pub completion_item: Option<CompletionOptionsItem>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompletionOptionsItem {
    #[serde(rename = "labelDetailsSupport")]
    pub label_details_support: bool,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ServerInfo {
    pub name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,
}

#[cfg(test)]
mod test {
    const JSON: &str = r#" {
  "capabilities": {
    "general": {
      "positionEncodings": [
        "utf-8",
        "utf-16",
        "utf-32"
      ]
    },
    "textDocument": {
      "callHierarchy": {
        "dynamicRegistration": false
      },
      "codeAction": {
        "codeActionLiteralSupport": {
          "codeActionKind": {
            "valueSet": [
              "",
              "quickfix",
              "refactor",
              "refactor.extract",
              "refactor.inline",
              "refactor.rewrite",
              "source",
              "source.organizeImports"
            ]
          }
        },
        "dataSupport": true,
        "disabledSupport": true,
        "dynamicRegistration": true,
        "honorsChangeAnnotations": true,
        "isPreferredSupport": true,
        "resolveSupport": {
          "properties": [
            "edit",
            "command"
          ]
        }
      },
      "codeLens": {
        "dynamicRegistration": false,
        "resolveSupport": {
          "properties": [
            "command"
          ]
        }
      },
      "colorProvider": {
        "dynamicRegistration": true
      },
      "completion": {
        "completionItem": {
          "commitCharactersSupport": false,
          "deprecatedSupport": true,
          "documentationFormat": [
            "markdown",
            "plaintext"
          ],
          "insertReplaceSupport": true,
          "labelDetailsSupport": true,
          "preselectSupport": false,
          "resolveSupport": {
            "properties": [
              "additionalTextEdits",
              "command",
              "documentation"
            ]
          },
          "snippetSupport": true,
          "tagSupport": {
            "valueSet": [
              1
            ]
          }
        },
        "completionItemKind": {
          "valueSet": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25
          ]
        },
        "completionList": {
          "itemDefaults": [
            "editRange",
            "insertTextFormat",
            "insertTextMode",
            "data"
          ]
        },
        "contextSupport": true,
        "dynamicRegistration": false
      },
      "declaration": {
        "linkSupport": true
      },
      "definition": {
        "dynamicRegistration": true,
        "linkSupport": true
      },
      "diagnostic": {
        "dataSupport": true,
        "dynamicRegistration": true,
        "relatedDocumentSupport": true,
        "relatedInformation": true,
        "tagSupport": {
          "valueSet": [
            1,
            2
          ]
        }
      },
      "documentHighlight": {
        "dynamicRegistration": false
      },
      "documentLink": {
        "dynamicRegistration": false,
        "tooltipSupport": false
      },
      "documentSymbol": {
        "dynamicRegistration": false,
        "hierarchicalDocumentSymbolSupport": true,
        "symbolKind": {
          "valueSet": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25,
            26
          ]
        },
        "tagSupport": {
          "valueSet": [
            1
          ]
        }
      },
      "foldingRange": {
        "dynamicRegistration": false,
        "foldingRange": {
          "collapsedText": true
        },
        "foldingRangeKind": {
          "valueSet": [
            "comment",
            "imports",
            "region"
          ]
        },
        "lineFoldingOnly": true
      },
      "formatting": {
        "dynamicRegistration": true
      },
      "hover": {
        "contentFormat": [
          "markdown",
          "plaintext"
        ],
        "dynamicRegistration": true
      },
      "implementation": {
        "linkSupport": true
      },
      "inlayHint": {
        "dynamicRegistration": true,
        "resolveSupport": {
          "properties": [
            "textEdits",
            "tooltip",
            "location",
            "command"
          ]
        }
      },
      "inlineCompletion": {
        "dynamicRegistration": false
      },
      "linkedEditingRange": {
        "dynamicRegistration": false
      },
      "onTypeFormatting": {
        "dynamicRegistration": false
      },
      "publishDiagnostics": {
        "dataSupport": true,
        "relatedInformation": true,
        "tagSupport": {
          "valueSet": [
            1,
            2
          ]
        }
      },
      "rangeFormatting": {
        "dynamicRegistration": true,
        "rangesSupport": true
      },
      "references": {
        "dynamicRegistration": false
      },
      "rename": {
        "dynamicRegistration": true,
        "honorsChangeAnnotations": true,
        "prepareSupport": true
      },
      "selectionRange": {
        "dynamicRegistration": false
      },
      "semanticTokens": {
        "augmentsSyntaxTokens": true,
        "dynamicRegistration": false,
        "formats": [
          "relative"
        ],
        "multilineTokenSupport": true,
        "overlappingTokenSupport": true,
        "requests": {
          "full": {
            "delta": true
          },
          "range": true
        },
        "serverCancelSupport": false,
        "tokenModifiers": [
          "declaration",
          "definition",
          "readonly",
          "static",
          "deprecated",
          "abstract",
          "async",
          "modification",
          "documentation",
          "defaultLibrary"
        ],
        "tokenTypes": [
          "namespace",
          "type",
          "class",
          "enum",
          "interface",
          "struct",
          "typeParameter",
          "parameter",
          "variable",
          "property",
          "enumMember",
          "event",
          "function",
          "method",
          "macro",
          "keyword",
          "modifier",
          "comment",
          "string",
          "number",
          "regexp",
          "operator",
          "decorator"
        ]
      },
      "signatureHelp": {
        "dynamicRegistration": false,
        "signatureInformation": {
          "activeParameterSupport": true,
          "documentationFormat": [
            "markdown",
            "plaintext"
          ],
          "noActiveParameterSupport": true,
          "parameterInformation": {
            "labelOffsetSupport": true
          }
        }
      },
      "synchronization": {
        "didSave": true,
        "dynamicRegistration": false,
        "willSave": true,
        "willSaveWaitUntil": true
      },
      "typeDefinition": {
        "linkSupport": true
      }
    },
    "window": {
      "showDocument": {
        "support": true
      },
      "showMessage": {
        "messageActionItem": {
          "additionalPropertiesSupport": true
        }
      },
      "workDoneProgress": true
    },
    "workspace": {
      "applyEdit": true,
      "codeLens": {
        "refreshSupport": true
      },
      "configuration": true,
      "diagnostics": {
        "refreshSupport": true
      },
      "didChangeConfiguration": {
        "dynamicRegistration": false
      },
      "didChangeWatchedFiles": {
        "dynamicRegistration": false,
        "relativePatternSupport": true
      },
      "fileOperations": {
        "didCreate": false,
        "didDelete": false,
        "didRename": false,
        "dynamicRegistration": false,
        "willCreate": false,
        "willDelete": false,
        "willRename": false
      },
      "inlayHint": {
        "refreshSupport": true
      },
      "semanticTokens": {
        "refreshSupport": true
      },
      "symbol": {
        "dynamicRegistration": false,
        "symbolKind": {
          "valueSet": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25,
            26
          ]
        }
      },
      "workspaceEdit": {
        "changeAnnotationSupport": {
          "groupsOnLabel": true
        },
        "normalizesLineEndings": true,
        "resourceOperations": [
          "rename",
          "create",
          "delete"
        ]
      },
      "workspaceFolders": true
    }
  },
  "clientInfo": {
    "name": "Neovim",
    "version": "0.12.2+v0.12.2"
  },
  "processId": 266394,
  "rootPath": null,
  "rootUri": null,
  "trace": "off",
  "workDoneToken": "1",
  "workspaceFolders": null
}
"#;

    use super::*;

    #[test]
    fn test() {
        serde_json::from_str::<InitializeParams>(JSON).unwrap();
    }
}
