use crate::hyperware::process;
use anyhow::{anyhow, Result};
use hyperware::process::standard::send_response;
use hyperware_process_lib::net::{NetAction, NetResponse};
use hyperware_process_lib::{
    await_message, call_init, eth, get_blob, get_typed_state, homepage, http, hypermap, kiprintln,
    set_state, Address, Capability, LazyLoadBlob, Message, NodeId, Request, Response,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::str::FromStr;

wit_bindgen::generate!({
    path: "target/wit",
    world: "login-sys-v0",
    generate_unused_types: true,
    additional_derives: [PartialEq, serde::Deserialize, serde::Serialize, process_macros::SerdeJsonInto],
});

#[derive(Debug, Serialize, Deserialize)]
struct LoginStateV1 {
    our: Address,
    apps: HashMap<NodeId, String>,
}

#[derive(Debug, Serialize, Deserialize)]
enum LoginRequest {
    Sign(SignRequest),
    Verify { from: Address, data: SignResponse },
}
#[derive(Debug, Serialize, Deserialize)]
struct SignRequest {
    pub site: String,
    pub time: u64,
    pub nonce: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct SignResponse {
    pub body: SignRequest,
    pub message: Vec<u8>,
    pub signature: Vec<u8>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "version")]
enum VersionedState {
    /// State fully stored in memory, persisted using serde_json.
    /// Future state version will use SQLite.
    V1(LoginStateV1),
}

impl VersionedState {
    fn load(our: Address) -> Self {
        get_typed_state(|bytes| serde_json::from_slice(bytes)).unwrap_or(Self::V1(LoginStateV1 {
            our,
            apps: HashMap::new(),
        }))
    }

    fn save(&self) {
        set_state(&serde_json::to_vec(&self).expect("Failed to serialize contacts state!"));
    }
}

call_init!(initialize);
fn initialize(our: Address) {
    let mut state = VersionedState::load(our.clone());
    loop {
        let msg = await_message();
        match msg {
            Err(_send_error) => {
                // ignore send errors, local-only process
                continue;
            }
            Ok(Message::Request {
                source,
                body,
                capabilities,
                ..
            }) => {
                handle_request(&our, &source, &mut state, &body, capabilities).unwrap_or_default()
            }
            _ => continue, // ignore responses
        }
    }
}

fn handle_request(
    our: &Address,
    source: &Address,
    state: &mut VersionedState,
    request_bytes: &[u8],
    capabilities: Vec<Capability>,
) -> Result<()> {
    let req = serde_json::from_slice::<LoginRequest>(request_bytes)?;
    match req {
        LoginRequest::Sign(r) => handle_sign(our, r, request_bytes),
        LoginRequest::Verify { from, data } => handle_verify(from, data),
    }
}

// let message = [
//     from.to_string().as_bytes(),
//     &km.lazy_load_blob
//         .as_ref()
//         .unwrap_or(&lib::core::LazyLoadBlob {
//             mime: None,
//             bytes: vec![],
//         })
//         .bytes,
// ]
// .concat();
// pub fn validate_signature(from: &str, signature: &[u8], message: &[u8], pki: &OnchainPKI) -> bool {
//     if let Some(peer_id) = pki.get(from) {
//         let their_networking_key = signature::UnparsedPublicKey::new(
//             &signature::ED25519,
//             net_key_string_to_hex(&peer_id.networking_key),
//         );
//         their_networking_key.verify(message, signature).is_ok()
//     } else {
//         false
//     }
// }

fn handle_sign(our: &Address, req: SignRequest, request_bytes: &[u8]) -> Result<()> {
    let body = rmp_serde::to_vec(&NetAction::Sign)?;
    let res = Request::to(("our", "net", "distro", "sys"))
        .blob_bytes(request_bytes)
        .body(body)
        .send_and_await_response(10)??;
    let Ok(NetResponse::Signed) = rmp_serde::from_slice::<NetResponse>(res.body()) else {
        return Err(anyhow!("signature failed"));
    };
    let newblob = res.blob();
    let message = [our.to_string().as_bytes(), request_bytes].concat();
    match newblob {
        None => Err(anyhow!("no blob")),
        Some(b) => {
            let lr = SignResponse {
                body: req,
                message,
                signature: b.bytes().to_vec(),
            };
            let lrj = serde_json::to_vec(&lr)?;
            Response::new().body(lrj).send()?;
            Ok(())
        }
    }
}
fn handle_verify(from: Address, data: SignResponse) -> Result<()> {
    let signature = data.signature;
    let body = rmp_serde::to_vec(&NetAction::Verify { from, signature })?;
    let req_bytes = rmp_serde::to_vec(&data.body)?;
    let res = Request::to(("our", "net", "distro", "sys"))
        .blob_bytes(req_bytes)
        .body(body)
        .send_and_await_response(10)??;
    let resp = rmp_serde::from_slice::<NetResponse>(res.body())?;
    match resp {
        NetResponse::Verified(is_good) => {
            Response::new().body(serde_json::to_vec(&is_good)?).send()?;
            Ok(())
        }
        _ => Err(anyhow!("weird response")),
    }
}
