// TODO: Currently not working, Authorization failure

#[cfg(feature = "wasm-bindings")]
use crate::client::BoxedProfile;
use crate::{parse::load_factor::HafasLoadFactor, LoadFactor, ParseResult, Product, Profile};
use serde_json::{json, Value};
use std::collections::HashMap;
#[cfg(feature = "wasm-bindings")]
use wasm_bindgen::prelude::wasm_bindgen;

// TODO: Load factor

mod products {
    use crate::{Mode, Product};
    use std::borrow::Cow;

    pub const HIGH_SPEED: Product = Product {
        id: Cow::Borrowed("high-speed-train"),
        mode: Mode::Train,
        bitmasks: Cow::Borrowed(&[1, 2, 4, 8]),
        name: Cow::Borrowed("High Speed Train"),
        short: Cow::Borrowed("High Speed"),
    };
    pub const URBAN: Product = Product {
        id: Cow::Borrowed("urban-train"),
        mode: Mode::Train,
        bitmasks: Cow::Borrowed(&[32]),
        name: Cow::Borrowed("Urban Train"),
        short: Cow::Borrowed("Urban"),
    };
    pub const TRAM: Product = Product {
        id: Cow::Borrowed("tram"),
        mode: Mode::Train,
        bitmasks: Cow::Borrowed(&[512]),
        name: Cow::Borrowed("Tram"),
        short: Cow::Borrowed("Tram"),
    };
    pub const BUS: Product = Product {
        id: Cow::Borrowed("bus"),
        mode: Mode::Bus,
        bitmasks: Cow::Borrowed(&[64]),
        name: Cow::Borrowed("Bus"),
        short: Cow::Borrowed("Bus"),
    };
    pub const BOAT: Product = Product {
        id: Cow::Borrowed("boat"),
        mode: Mode::Watercraft,
        bitmasks: Cow::Borrowed(&[16]),
        name: Cow::Borrowed("Boat"),
        short: Cow::Borrowed("Boat"),
    };
    pub const CABLE_CAR: Product = Product {
        id: Cow::Borrowed("cable-car"),
        mode: Mode::Gondola,
        bitmasks: Cow::Borrowed(&[128]),
        name: Cow::Borrowed("Cable Car"),
        short: Cow::Borrowed("Cable Car"),
    };
    pub const NIGHT_TRAIN: Product = Product {
        id: Cow::Borrowed("night-train"),
        mode: Mode::Train,
        bitmasks: Cow::Borrowed(&[256]),
        name: Cow::Borrowed("Night Train"),
        short: Cow::Borrowed("Night Train"),
    };

    pub const PRODUCTS: &[&Product] = &[
        &HIGH_SPEED,
        &URBAN,
        &TRAM,
        &BUS,
        &BOAT,
        &CABLE_CAR,
        &NIGHT_TRAIN,
    ];
}

#[derive(Debug)]
pub struct ZvvProfile;

impl Profile for ZvvProfile {
    fn url(&self) -> &'static str {
        "https://online.fahrplan.zvv.ch/bin/mgate.exe"
    }
    fn language(&self) -> &'static str {
        "de"
    }
    fn timezone(&self) -> chrono_tz::Tz {
        chrono_tz::Europe::Zurich
    }
    fn checksum_salt(&self) -> Option<&'static str> {
        None
    }
    fn refresh_journey_use_out_recon_l(&self) -> bool {
        true
    }

    fn parse_load_factor(&self, h: HafasLoadFactor) -> ParseResult<LoadFactor> {
        // TODO: Load factors correct?
        match h {
            10 => Ok(LoadFactor::LowToMedium),
            11 => Ok(LoadFactor::High),
            12 => Ok(LoadFactor::VeryHigh),
            13 => Ok(LoadFactor::ExceptionallyHigh),
            _ => Err(format!("Invalid load factor: {}", h).into()),
        }
    }

    fn products(&self) -> &'static [&'static Product] {
        products::PRODUCTS
    }

    fn prepare_body(&self, req_json: &mut Value) {
        req_json["client"] = json!({"type":"IPH","id":"ZVV","v":"6000400","name":"zvvPROD-STORE"});
        req_json["ver"] = json!("1.42");
        req_json["ext"] = json!("ZVV.2");
        req_json["auth"] = json!({"type":"AID","aid":"TLRUqdDPF7ttB824Yoy2BN8mk"});
    }

    fn prepare_headers(&self, headers: &mut HashMap<&str, &str>) {
        headers.insert("User-Agent", "my-awesome-e5f276d8fe6cprogram");
    }

    fn price_currency(&self) -> &'static str {
        "EUR"
    }
}

#[cfg(feature = "wasm-bindings")]
#[wasm_bindgen]
impl ZvvProfile {
    #[wasm_bindgen(constructor)]
    pub fn wasm_new() -> BoxedProfile {
        Self.into()
    }
}

#[cfg(test)]
mod test {
    use crate::{
        api::journeys::JourneysOptions, client::HafasClient,
        requester::hyper::HyperRustlsRequester, Place, Stop,
    };
    use std::error::Error;

    use super::*;

    #[tokio::test]
    async fn test_path_available() -> Result<(), Box<dyn Error>> {
        let client = HafasClient::new(ZvvProfile {}, HyperRustlsRequester::new());
        let journeys = client
            .journeys(
                Place::Stop(Stop {
                    id: "8591105".to_string(),
                    ..Default::default()
                }),
                Place::Stop(Stop {
                    id: "8591123".to_string(),
                    ..Default::default()
                }),
                JourneysOptions::default(),
            )
            .await?;
        assert!(!journeys.journeys.is_empty());
        Ok(())
    }
}
