use btleplug::api::{Central, Manager as _, Peripheral as _, ScanFilter};
use btleplug::platform::Manager;
use chrono::{Datelike, Local, Timelike};
use infinitime_rs;
use std::error::Error;
use std::time::Duration;
use tokio::time;

mod metno;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let manager = Manager::new().await.unwrap();

    let adapters = manager.adapters().await?;
    let central_adapter = adapters.into_iter().nth(0).unwrap();

    central_adapter.start_scan(ScanFilter::default()).await?;
    time::sleep(Duration::from_secs(2)).await;

    let infinitime = infinitime_rs::find_infinitime(&central_adapter)
        .await
        .unwrap();
    println!("Found watch!");
    infinitime.connect().await?;
    println!("Connected to watch!");
    infinitime.discover_services().await?;

    println!(
        "Firmware version: {}",
        infinitime_rs::get_firmware_version(&infinitime).await
    );
    println!(
        "Battery level: {}",
        infinitime_rs::get_battery_level(&infinitime).await
    );
    println!(
        "Heart rate: {}",
        infinitime_rs::get_heart_rate(&infinitime).await
    );
    println!(
        "Step count: {}",
        infinitime_rs::get_step_count(&infinitime).await
    );
    infinitime_rs::send_notification(
        &infinitime,
        infinitime_rs::Notification::new(
            infinitime_rs::NotificationCategory::SimpleAlert,
            "test".to_string(),
            "body".to_string(),
        ),
    )
    .await;
    let client = metno::MetNoClient::new();
    let entries = client
        .fetch(45.50884, -73.58781) // Montreal
        .map_err(|e| format!("failed to fetch data from MET.NO: {e}"))?;

    let current = metno::process_current(&entries)
        .map_err(|e| format!("failed to process current weather: {e}"))?;
    let forecast = metno::process_forecast(&entries)
        .map_err(|e| format!("failed to process forecast: {e}"))?;
    let now = Local::now();
    let _ = infinitime_rs::send_current_weather(
        &infinitime,
        now.timestamp().try_into().unwrap_or_default(),
        current.temperature as i16,
        current.min_temperature as i16,
        current.max_temperature as i16,
        "Montreal",
        current.icon,
        // 5 * 60,
        // 21 * 60,
    )
    .await;
    let _ = infinitime_rs::send_forecast(
        &infinitime,
        now.timestamp().try_into().unwrap_or_default(),
        5,
        &[
            (
                forecast[0].min_temperature.round() as i16,
                forecast[0].max_temperature.round() as i16,
                forecast[0].icon,
            ),
            (
                forecast[1].min_temperature.round() as i16,
                forecast[1].max_temperature.round() as i16,
                forecast[1].icon,
            ),
            (
                forecast[2].min_temperature.round() as i16,
                forecast[2].max_temperature.round() as i16,
                forecast[2].icon,
            ),
            (
                forecast[3].min_temperature.round() as i16,
                forecast[3].max_temperature.round() as i16,
                forecast[3].icon,
            ),
            (
                forecast[4].min_temperature.round() as i16,
                forecast[4].max_temperature.round() as i16,
                forecast[4].icon,
            ),
        ],
    )
    .await;
    let _ = infinitime_rs::send_current_time(
        &infinitime,
        now.year().try_into().unwrap_or_default(),
        now.month().try_into().unwrap_or_default(),
        now.day().try_into().unwrap_or_default(),
        now.hour().try_into().unwrap_or_default(),
        now.minute().try_into().unwrap_or_default(),
        now.second().try_into().unwrap_or_default(),
        now.weekday()
            .number_from_sunday()
            .try_into()
            .unwrap_or_default(),
        now.timestamp_subsec_micros().try_into().unwrap_or_default(),
    )
    .await;
    let _ = infinitime_rs::send_navigation(
        &infinitime,
        &infinitime_rs::NavigationInstruction::new(
            "off-ramp-right".to_string(),
            "At the roundabout take the first exit".to_string(),
            "50 m".to_string(),
            10,
        ),
    )
    .await;

    Ok(())
}