use serde::{Deserialize, Serialize, de::DeserializeOwned};
use serde_json::Value;
use serde_repr::{Deserialize_repr, Serialize_repr};

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Position {
    pub line: usize,
    pub character: usize,
}

impl Position {
    /// Given the full document text and the index of the whole text,
    /// return a position in form of line + character offet of that line.
    /// Line and offset are 0-based.
    #[must_use]
    pub fn from_text_and_index(text: &str, index: usize) -> Self {
        let mut remainder = index;
        let mut line_ct = 0;
        let lines = text.split('\n');
        for line in lines {
            if line.len() >= remainder {
                break;
            }
            remainder -= line.len();
            line_ct += 1;
        }
        Self {
            line: line_ct,
            character: remainder,
        }
    }
}

#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Range {
    pub start: Position,
    pub end: Position,
}

impl Range {
    /// Returns a range from a range format that is absolute of the file.
    /// This is composing the effect of [`Position::from_text_and_index`].
    #[must_use]
    pub fn from_text_position_range(text: &str, indices: core::ops::Range<usize>) -> Self {
        Range {
            start: Position::from_text_and_index(text, indices.start),
            end: Position::from_text_and_index(text, indices.end),
        }
    }
}

impl From<core::ops::Range<Position>> for Range {
    fn from(value: core::ops::Range<Position>) -> Self {
        Self {
            start: value.start,
            end: value.end,
        }
    }
}

const JSONRPC: &str = "2.0";

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Request {
    jsonrpc: String,
    pub id: i32,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
}

impl Request {
    #[must_use]
    pub fn new(id: i32, method: &str, params: Value) -> Self {
        Self {
            jsonrpc: JSONRPC.to_string(),
            id,
            method: method.to_string(),
            params: if params.is_null() { None } else { Some(params) },
        }
    }

    /// This is a shorthand for a method not found error.
    #[must_use]
    pub fn not_found(&self) -> Response {
        Response::error(self.id, ResponseError::MethodNotFound)
    }

    #[must_use]
    pub fn into_params<T: DeserializeOwned + Default>(&self) -> T {
        serde_json::from_value::<T>(self.params.clone().unwrap_or(Value::Null)).unwrap_or_default()
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Response {
    jsonrpc: String,
    pub id: i32,
    pub result: Option<Value>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<ResponseError>,
}

impl Response {
    /// A normal response with the payload.
    #[must_use]
    pub fn new(id: i32, value: Value) -> Self {
        Self {
            jsonrpc: JSONRPC.to_string(),
            id,
            result: if value.is_null() { None } else { Some(value) },
            error: None,
        }
    }

    /// Create an error response
    #[must_use]
    pub fn error(id: i32, error: ResponseError) -> Self {
        Self {
            jsonrpc: JSONRPC.to_string(),
            id,
            result: None,
            error: Some(error),
        }
    }
}

impl<T: Serialize> From<(i32, Result<T, Response>)> for Response {
    fn from(value: (i32, Result<T, Response>)) -> Self {
        match value.1 {
            Ok(t) => Response::new(
                value.0,
                serde_json::to_value(t).expect("Internal object was not serialiable"),
            ),
            Err(r) => r,
        }
    }
}

#[repr(i32)]
#[derive(Clone, Debug, PartialEq, Deserialize_repr, Serialize_repr)]
pub enum ResponseError {
    ParseError = -32700,
    InvalidRequest = -32600,
    MethodNotFound = -32601,
    InvalidParams = -32602,
    InternalError = -32603,
    ServerNotInitialized = -32002,
    UnknownErrorCode = -32001,
    RequestFailed = -32803,
    ServerCancelled = -32802,
    ContentModified = -32801,
    RequestCancelled = -32800,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Notification {
    jsonrpc: String,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
}

impl Notification {
    #[must_use]
    pub fn new(method: &str, params: Value) -> Self {
        Self {
            jsonrpc: JSONRPC.to_string(),
            method: method.to_string(),
            params: if params.is_null() { None } else { Some(params) },
        }
    }

    #[must_use]
    pub fn empty(method: &str) -> Self {
        Self {
            jsonrpc: JSONRPC.to_string(),
            method: method.to_string(),
            params: None,
        }
    }

    #[must_use]
    pub fn into_params<T: DeserializeOwned + Default>(&self) -> T {
        serde_json::from_value::<T>(self.params.clone().unwrap_or(Value::Null)).unwrap_or_default()
    }
}

impl<T: Serialize> From<(&str, T)> for Notification {
    fn from(value: (&str, T)) -> Self {
        let params = serde_json::to_value::<T>(value.1).unwrap();
        Self {
            jsonrpc: JSONRPC.to_string(),
            method: value.0.to_string(),
            params: Some(params),
        }
    }
}
