use feed_rs::model::Entry;
use feed_rs::parser;
use futures::future::join_all;
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
use maud::html;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::env;
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    iat: usize,
    exp: usize,
    aud: String,
}

#[derive(Serialize, Debug)]
struct PostPayload {
    posts: Vec<Post>,
}

#[derive(Serialize, Debug, Clone)]
struct Post {
    title: String,
    slug: String,
    html: String,
    status: String,
    published_at: String,
    updated_at: String,
    feature_image: String,
    canonical_url: String,
    tags: Vec<String>,
}

impl Post {
    fn new(
        title: &str,
        summary: &str,
        link: &str,
        mut categories: Vec<&str>,
        image_url: &str,
        pub_date: &str,
    ) -> Self {
        let html = html! {
            p { (summary) }
            iframe src=(link) style="width: 100%; height: 80vh" { }
            p {
                "This content was originally posted on my projects website " a href=(link) { "here." }
                " The above summary was made by the " a href=("https://help.kagi.com/kagi/api/summarizer.html")
                {"Kagi Summarizer"}
            }
        };

        let slug = get_slug(link);
        categories.push("Projects Website");
        Post {
            title: title.to_string(),
            slug: slug.to_string(),
            html: html.into_string(),
            status: "published".to_string(),
            published_at: pub_date.to_string(),
            updated_at: chrono::Utc::now().to_rfc3339(),
            feature_image: image_url.to_string(),
            canonical_url: link.to_string(),
            tags: categories.into_iter().map(|c| c.to_string()).collect(),
        }
    }
}

async fn entry_to_post(entry: Entry) -> Post {
    let link = entry.links.first().unwrap().href.as_str();
    let summary = summarize_url(link).await;

    Post::new(
        entry.title.as_ref().unwrap().content.as_str(),
        summary.as_str(),
        link,
        entry
            .categories
            .iter()
            .map(|category| category.term.as_str())
            .collect::<Vec<&str>>(),
        entry
            .media
            .first()
            .and_then(|m| m.content.first())
            .and_then(|c| c.url.as_ref().map(|u| u.as_str()))
            .unwrap_or_default(),
        &entry.published.unwrap().to_rfc3339(),
    )
}

fn get_slug(link: &str) -> String {
    link.split_once("/posts/").unwrap().1.to_string()
}

async fn check_if_post_exists(entry: &Entry) -> bool {
    let posts_url = "https://notes.ansonbiggs.com/";
    let link = entry.links.first().unwrap().href.as_str();
    let slug = get_slug(link);

    match reqwest::get(format!("{}{}", posts_url, slug)).await {
        Ok(response) => response.status().is_success(),
        Err(_) => false,
    }
}

async fn fetch_feed(url: &str) -> Vec<Entry> {
    let content = reqwest::get(url).await.unwrap().text().await.unwrap();

    let feed = parser::parse(content.as_bytes()).unwrap();

    feed.entries
}

#[derive(Serialize)]
struct KagiPayload {
    url: String,
    engine: String,
    summary_type: String,
    target_language: String,
    cache: bool,
}
impl KagiPayload {
    fn new(url: &str) -> Self {
        // https://help.kagi.com/kagi/api/summarizer.html
        KagiPayload {
            url: url.to_string(),
            engine: "agnes".to_string(), // Formal, technical, analytical summary
            summary_type: "summary".to_string(),
            target_language: "EN".to_string(),
            cache: true,
        }
    }
}

#[derive(Deserialize)]
struct KagiResponse {
    data: Data,
}

#[derive(Deserialize)]
struct Data {
    output: String,
}

async fn summarize_url(url: &str) -> String {
    let kagi_url = "https://kagi.com/api/v0/summarize";
    let kagi_api_key = env::var("kagi_api_key").unwrap();

    let payload = KagiPayload::new(url);

    let client = Client::new();

    let response = client
        .post(kagi_url)
        .header("Authorization", format!("Bot {}", kagi_api_key))
        .json(&payload)
        .send()
        .await
        .expect("Kagi request failed");

    response
        .json::<KagiResponse>()
        .await
        .expect("Kagi didn't return json")
        .data
        .output
}
#[tokio::main]
async fn main() {
    let ghost_api_url = "https://notes.ansonbiggs.com/ghost/api/v3/admin/posts/?source=html";
    let ghost_admin_api_key = env::var("admin_api_key").unwrap();

    let feed = "https://projects.ansonbiggs.com/index.xml";

    // Split the key into ID and SECRET
    let (id, secret) = ghost_admin_api_key
        .split_once(':')
        .expect("Invalid API key format");

    // Prepare JWT header and claims
    let iat = chrono::Utc::now().timestamp() as usize;
    let exp = iat + 5 * 60;
    let claims = Claims {
        iat,
        exp,
        aud: "v3/admin".into(),
    };

    let header = Header {
        alg: Algorithm::HS256,
        kid: Some(id.into()),
        ..Default::default()
    };

    // Encode the JWT
    let token = encode(
        &header,
        &claims,
        &EncodingKey::from_secret(&hex::decode(secret).expect("Invalid secret hex")),
    )
    .expect("JWT encoding failed");

    // Prepare the post data
    let entries = fetch_feed(feed).await;

    let post_exists_futures = entries.into_iter().map(|entry| {
        let entry_clone = entry.clone(); // Clone entry if necessary (depends on your data structure)
        async move { (entry_clone, check_if_post_exists(&entry).await) }
    });
    let post_exists_results = join_all(post_exists_futures).await;
    let filtered_entries: Vec<Entry> = post_exists_results
        .into_iter()
        .filter_map(|(entry, exists)| if !exists { Some(entry) } else { None })
        .collect();

    if filtered_entries.is_empty() {
        println!("Nothing to post.");
        return;
    }

    let post_futures = filtered_entries.into_iter().map(entry_to_post);

    let client = Client::new();

    for post in join_all(post_futures).await {
        let post_payload = PostPayload {
            posts: vec![post.clone()],
        };

        let response = client
            .post(ghost_api_url)
            .header("Authorization", format!("Ghost {}", token))
            .json(&post_payload)
            .send()
            .await
            .expect("Request failed");

        // Check the response
        if response.status().is_success() {
            println!("Post {} published successfully.", post.title);
        } else {
            println!(
                "Failed to publish post {}. Status: {:?}",
                &post.title,
                response.status()
            );
        }
    }
}
