use crate::{commands::utils, Context, Error};
use cached::proc_macro::cached;
use chrono::{DateTime, Utc};
use log::warn;
use reqwest::header::AUTHORIZATION;
use serde::Deserialize;

#[derive(Deserialize, Clone)]
struct MSReq {
    containers: Vec<MSEvent>,
    #[serde(rename = "eventTitle")]
    event_title: String,
}

#[derive(Deserialize, Clone)]
struct MSEvent {
    id: String,
    // #[serde(rename = "longDescription")]
    // description: String,
    // country: String,
    metadata: MSMetadata,
}

impl MSEvent {
    fn get_title(&self) -> String {
        let title = &self.metadata.attributes.series.replace("FORMULA", "F");
        format!("**{}: {}**", title, self.metadata.brief)
        // format!("", sport=self.content.format.sport, title=self.content.title, synopsis=self.content.synopsis, start=self.times.start.timestamp(), end=self.times.end.timestamp(), desc=self.content.description, id=self.system.product_key)
    }

    fn get_value(&self, high_tier: bool) -> String {
        let link = if high_tier {
            format!(
                "[{id}](https://morningstreams.com/hightier/f1/session/{id})\n",
                id = self.id
            )
        } else {
            "".to_string()
        };
        format!(
            "{link}Start: <t:{start}:R>\nEnd:  <t:{end}:R>",
            link = link,
            start = self.metadata.attributes.start.timestamp(),
            end = self.metadata.attributes.end.timestamp()
        )
    }
}

#[derive(Deserialize, Clone)]
struct MSMetadata {
    // id: String,
    // system: System,
    #[serde(rename = "emfAttributes")]
    attributes: EmfAttributes,
    #[serde(rename = "titleBrief")]
    brief: String,
    // #[serde(rename="Series")]
    // series: String,
}

#[derive(Deserialize, Clone)]
struct EmfAttributes {
    #[serde(with = "ms_date")]
    #[serde(rename = "sessionStartDate")]
    start: DateTime<Utc>,
    #[serde(with = "ms_date")]
    #[serde(rename = "sessionEndDate")]
    end: DateTime<Utc>,
    #[serde(rename = "Series")]
    series: String,
}

mod ms_date {
    use chrono::{DateTime, NaiveDateTime, Utc};
    use serde::{self, Deserialize, Deserializer};

    pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let n = i64::deserialize(deserializer)? / 1000;
        // let s = String::deserialize(deserializer)?;

        Ok(DateTime::from_utc(NaiveDateTime::from_timestamp(n, 0), Utc))
    }
}

#[cached(time = 3600)]
async fn get_schedule() -> Option<MSReq> {
    let token = super::super::SETTINGS
        .read()
        .unwrap()
        .get_table("morningstreams")
        .unwrap()
        .get("token")
        .expect("Config error, please set the morningstreams[token] value")
        .clone()
        .into_string()
        .expect("Config error, please make sure morningstreams[token] is a string");
    let client = reqwest::Client::new();
    let req = client
        .get(format!(
            "https://api.morningstreams.com/api/hightier/f1/next-event"
        ))
        .header(AUTHORIZATION, token)
        .send()
        .await;

    let result: Option<MSReq> = match req {
        Err(e) => {
            warn!("Error getting normal schedule {}", e);
            None
        }
        Ok(req) if req.status().as_u16() == 404 => {
            warn!("404 on getting schedule");
            None
        }
        Ok(req) if req.status().as_u16() == 200 => {
            let data = req.json::<MSReq>().await;
            match data {
                Ok(d) => Some(d),
                Err(e) => {
                    warn!("Error getting normal schedule {}", e);
                    None
                }
            }
        }
        Ok(req) => {
            warn!(
                "Unhandled status when parsing schedule request {}",
                req.status()
            );
            None
        }
    };
    result
}

#[poise::command(slash_command)]
pub async fn schedule(ctx: Context<'_>) -> Result<(), Error> {
    let events: Option<MSReq> = get_schedule().await;
    let ht: bool = utils::high_tier(ctx).await;
    match events {
        None => {
            ctx.say("Error on fetching events :(").await?;
        }
        Some(evs) => {
            ctx.send(|b| {
                b.embed(|e| {
                    e.title(format!("F1 schedule: {}", evs.event_title));
                    for event in evs.containers {
                        e.field(event.get_title(), event.get_value(ht), true);
                    }
                    e
                })
            })
            .await?;
        }
    };

    Ok(())
}