use reqwest::header::{HeaderMap, HeaderValue};
use reqwest::Client;
use crate::openai::Message;
use std::env;
use serde_derive::{Deserialize, Serialize};

// Input structures
// Chat
#[derive(Debug, Serialize, Clone)]
pub struct ChatCompletion {
    pub model: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub system: Option<String>,
    pub messages: Vec<Message>,
    pub temperature: f32,
    //pub top_p: f32,
    pub max_tokens: u32,
    //pub stream: bool,
    //pub safe_mode: bool,
    //pub random_seed: i32,
}

/*
#[derive(Debug, Serialize, Clone)]
pub struct Message {
    pub role: String,
    pub content: String,
}
*/

// Output structures
// Chat
#[derive(Debug, Deserialize)]
pub struct APIResponse {
    pub id: String,
    pub r#type: String,
    pub role: String,
    pub content: Option<Vec<Content>>,
    pub model: String,
    pub stop_reason: String,
    //pub usage: Usage,
}

#[derive(Debug, Deserialize)]
pub struct Content {
    pub r#type: String,
    pub text: String,
}

#[derive(Debug, Deserialize)]
pub struct Usage {
    pub input_tokens: usize,
    pub output_tokens: usize,
}

// Call Large Language Model
pub async fn call_anthropic(messages: Vec<Message>) -> Result<String, Box<dyn std::error::Error + Send>> {
    let model: String =
        env::var("ANTHROPIC_MODEL").expect("ANTHROPIC_MODEL not found in enviornment variables");
    // Extract API Key information
    let api_key: String =
        env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not found in environment variables");
    let version: String =
        env::var("ANTHROPIC_VERSION").expect("ANTHROPIC_VERSION not found in environment variables");
    let url: String =
        env::var("ANTHROPIC_URL").expect("ANTHROPIC_VERSION not found in environment variables");

    // Create headers
    let mut headers: HeaderMap = HeaderMap::new();

    let smess = extract_role("system", &messages);
    let umess = extract_role("user", &messages);

    // We would like json
    headers.insert(
        "Content-Type",
        HeaderValue::from_str("appication/json")
            .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?,
    );
    // Create api key header
    headers.insert(
        "x-api-key",
        HeaderValue::from_str(&api_key)
            .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?,
    );
    // Create api key header
    headers.insert(
        "anthropic-version",
        HeaderValue::from_str(&version)
            .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?,
    );

    // Create client
    let client: Client = Client::builder()
        .user_agent("TargetR")
        .timeout(std::time::Duration::new(90, 0))
        //.gzip(true)
        .default_headers(headers)
        .build()
        .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?;

    // Create chat completion
    let chat_completion: ChatCompletion = ChatCompletion {
        model,
        system: if smess.is_empty() { None } else { Some(smess) },
        messages: vec![Message { role: "user".into(), content: umess }],
        temperature: 0.2,
        max_tokens: 1024,
    };

//println!("{:?}", serde_json::to_string(&chat_completion));
    // Extract API Response
    let res = client
        .post(url)
        .json(&chat_completion)
        .send()
        .await;
//println!("### {:?}", res);
    let res: APIResponse = res
    //let res = res
        .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?
        .json()
        //.text()
        .await
        .map_err(|e| -> Box<dyn std::error::Error + Send> { Box::new(e) })?;
//println!("### {:?}", res);
    //let res: APIResponse = serde_json::from_str::<APIResponse>(&res).unwrap();

    // Send Response
    match res.content {
        Some(content) => {
            let text = content.iter().map(|s| s.text.lines().filter(|l| !l.starts_with("```")).fold(String::new(), |s, l| s + l + "\n")).collect();

            Ok(text)
        },
        None => {
            Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, "No content found")))
        }
    }
//Ok("".to_string())
}

fn extract_role(role: &str, messages: &[Message]) -> String {
    messages.iter()
        .filter(|m| role == m.role)
        .fold(String::new(), |mut s, i| {
            if !s.is_empty() {
                s.push('\n');
            }
            s.push_str(&i.content);

            s
        })
}

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

    #[tokio::test]
    async fn test_call_anthropic() {
        let messages: Vec<Message> = vec![Message { role: "user".into(), content: "Hello Claude".into() }];
        //let messages: Vec<Message> = vec![Message { role: "user".into(), content: "What is the meaining of life?".into() }];
        match call_anthropic(messages).await {
            Ok(answer) => { println!("{answer}"); assert!(true) },
            Err(e) => { println!("{e}"); assert!(false) },
        }
    }
}
