use std::time::Duration;

use chrono::Local;
use reqwest::StatusCode;
use serde::Serialize;
use tokio::time::sleep;

use crate::traewelling::{
    model::{JsonableData, Status, StopJourneyPart},
    RequestErr, TraewellingClient,
};

pub async fn get_current_journey() -> anyhow::Result<()> {
    let client = TraewellingClient::new()?;

    let mut state;
    let mut cur_active_checkin = None;

    loop {
        match client.get_active_checkin().await {
            Ok(status) => {
                cur_active_checkin = Some(status);
                state = State::Live;
            }
            Err(err) => {
                if err == RequestErr::WithStatus(StatusCode::NOT_FOUND) {
                    state = State::NoCheckin;
                    cur_active_checkin = None;
                } else {
                    state = State::NoConnectionOrSomethingElseDoesntWork;
                }
            }
        };

        match (state, &cur_active_checkin) {
            (State::Live | State::NoConnectionOrSomethingElseDoesntWork, Some(status)) => {
                let live = state == State::Live;
                let out = CurrentJourneyOutput::new(&status, live);

                println!(
                    "{}",
                    serde_json::to_string(&out)
                        .expect("serde should not make you sad but it does because it's serde")
                );
                sleep(Duration::from_secs(20)).await;
            }
            (_, None) | (State::NoCheckin, Some(_)) => {
                println!("null");
                sleep(Duration::from_secs(60)).await;
            }
        }
    }

    Ok(())
}

#[derive(PartialEq, Eq, Clone, Copy, Debug)]
enum State {
    Live,
    NoConnectionOrSomethingElseDoesntWork,
    NoCheckin,
}

#[derive(Serialize)]
struct CurrentJourneyOutput {
    live: bool,
    // Journey progress, 0.0-1.0
    progress: Option<f32>,
    time_left: Option<i64>,
    icon: String,
    line: String,

    // Invalid data received?
    departure_err: bool,
    departure_planned: Option<i64>,
    departure_real: Option<i64>,
    departure_station: String,
    departure_ril100: Option<String>,
    departure_platform_data_available: bool,
    departure_platform_planned: Option<String>,
    departure_platform_real: Option<String>,

    // Invalid data received?
    arrival_err: bool,
    arrival_planned: Option<i64>,
    arrival_real: Option<i64>,
    arrival_station: String,
    arrival_ril100: Option<String>,
    arrival_platform_data_available: bool,
    arrival_platform_planned: Option<String>,
    arrival_platform_real: Option<String>,
}

impl CurrentJourneyOutput {
    fn new(checkin: &Status, live: bool) -> Self {
        let JsonableData {
            time_err: departure_err,
            time_planned: departure_planned,
            time_real: departure_real,
            station: departure_station,
            ril100: departure_ril100,
            platform_data_available: departure_platform_data_available,
            platform_planned: departure_platform_planned,
            platform_real: departure_platform_real,
        } = checkin.train.origin.get_time_data(StopJourneyPart::Origin);
        let JsonableData {
            time_err: arrival_err,
            time_planned: arrival_planned,
            time_real: arrival_real,
            station: arrival_station,
            ril100: arrival_ril100,
            platform_data_available: arrival_platform_data_available,
            platform_planned: arrival_platform_planned,
            platform_real: arrival_platform_real,
        } = checkin
            .train
            .destination
            .get_time_data(StopJourneyPart::Destination);

        let (progress, time_left) = if !departure_err && !arrival_err {
            let departure = departure_real.unwrap_or(departure_planned.unwrap());
            let arrival = arrival_real.unwrap_or(arrival_planned.unwrap());
            let dur = arrival - departure;

            let now = Local::now().timestamp();

            let progress = ((now - departure) as f32) / dur as f32;
            let time_left = arrival - now;

            (Some(progress), Some(time_left))
        } else {
            (None, None)
        };

        let icon = match checkin.train.category.as_str() {
            "nationalExpress" | "national" => "longDistanceTrans",
            "regionalExp" | "regional" => "regionalTrans",
            "suburban" => "localTrans",
            "subway" => "subTrans",
            "bus" => "bus",
            "tram" => "tram",
            "ferry" => "ferry",
            _ => "other",
        }
        .to_string();

        CurrentJourneyOutput {
            live,
            progress,
            time_left,
            icon,
            line: checkin.train.line_name.clone(),
            departure_err,
            departure_planned,
            departure_real,
            departure_station,
            departure_ril100,
            departure_platform_data_available,
            departure_platform_planned,
            departure_platform_real,
            arrival_err,
            arrival_planned,
            arrival_real,
            arrival_station,
            arrival_ril100,
            arrival_platform_data_available,
            arrival_platform_planned,
            arrival_platform_real,
        }
    }
}

enum TransportType {
    // FV, ob jetzt NJ, IC, ICE... egal
    LongDistanceTrans,
    RegionalTrans,
    // S-bahn...
    LocalTrans,
    // U-bahn
    SubTrans,
    Bus,
    Tram,
    Ferry,
}
