Experiment with Rust
use tonic::{transport::{Server, ServerTlsConfig, Identity}, metadata::MetadataValue, Request, Response, Status};
use tonic::service::interceptor::InterceptedService;
use tonic_health::server::HealthReporter;
use rfkpos::rfkpos_server::{Rfkpos, RfkposServer};
use rfkpos::{CreateTransaction, GetTransaction, TransactionReturn, Database, Transaction, Purchase, transaction, Timestamp};
use std::time::Duration;
use std::sync::{Arc, Mutex};
use std::collections::LinkedList;
use std::io;
use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
use openssl::sign::{Signer, Verifier};
use openssl::ec::{EcKey, EcGroup, EcGroupRef};
use openssl::pkey::{ PKey, Private };
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use prost::Message;

mod rfkpos {
    include!("rfkpos.rs");

    pub(crate) const FILE_DESCRIPTOR_SET: &[u8] =
        tonic::include_file_descriptor_set!("greeter_descriptor");
}

#[derive(Default)]
pub struct RFKPOSImpl {
    db: Arc<Mutex<Database>>,
    tokens: Arc<Mutex<LinkedList<String>>>,
}

pub struct DataValidator {
    group: EcGroup,
    key_priv: PKey<Private>,
}

impl DataValidator {
    pub fn new() -> Result<Self, io::Error> {
        let group = EcGroup::from_curve_name(Nid::SECP256K1)?;
        let key = EcKey::generate(group.as_ref())?;
        let key_priv = PKey::from_ec_key(key)?;
        // let signer = Signer::new(MessageDigest::sha256(), &key_priv.as_ref()).unwrap();
        Ok(Self { group: group, key_priv: key_priv })
    }
}

fn new_token(tokens: Arc<Mutex<LinkedList<String>>>) -> String {
    let new_token: String = thread_rng().sample_iter(Alphanumeric).take(32).map(char::from).collect();
    let mut tokens = tokens.lock().unwrap();
    tokens.push_back(new_token.clone());
    while tokens.len() > 100 { tokens.pop_front(); }
    new_token
}

#[tonic::async_trait]
impl Rfkpos for RFKPOSImpl {
    async fn create(&self, request: Request<CreateTransaction>) -> Result<Response<TransactionReturn>, Status> {
        let remote_addr = request.remote_addr();
        let data = request.into_inner();
        println!("Request from {:?} {:?}", remote_addr, data);

        let data_validator = DataValidator::new();
        let mut transaction = data.transaction.ok_or_else(|| Status::invalid_argument("transaction not specified"))?;
        let tid = transaction.tid;

        self.db.lock().unwrap().transactions.insert(tid, transaction);

        Ok(Response::new(TransactionReturn {
            token: new_token(self.tokens.clone()),
            tid: tid,
        }))
    }

    async fn get(&self, request: Request<GetTransaction>) -> Result<Response<TransactionReturn>, Status> {
        println!("Request from {:?}", request.remote_addr());
        Err(Status::internal("Not implemented"))
    }
}

async fn rfkpos_service_status(mut reporter: HealthReporter) {
    loop {
        tokio::time::sleep(Duration::from_secs(1)).await;
        reporter.set_serving::<RfkposServer<RFKPOSImpl>>().await;
    }
}

struct Authenticator {
    token: String,
    db: Arc<Mutex<Database>>,
}

impl Authenticator {
    pub fn new(db: Arc<Mutex<Database>>) -> Self {
        Self { token: "Bearer test-token".to_string(), db: db }
    }

    fn get_token(&self) -> &str {
        self.token.as_str()
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051".parse().unwrap();
    let cert = tokio::fs::read("server.pem").await?;
    let key = tokio::fs::read("server.key").await?;
    let identity = Identity::from_pem(cert, key);
    let rfkpos = RFKPOSImpl::default();
    let authenticator = Arc::new(Mutex::new(Authenticator::new(rfkpos.db.clone())));
    let rfkpos_auth = Arc::clone(&authenticator);
    let rfkpos_svc = RfkposServer::with_interceptor(rfkpos, move |r| check_auth(r, rfkpos_auth.clone()));

    let reflection_service = tonic_reflection::server::Builder::configure()
        .register_encoded_file_descriptor_set(rfkpos::FILE_DESCRIPTOR_SET)
        .build()
        .unwrap();
    let reflection_service_auth = Arc::clone(&authenticator);
    let reflection_service_svc = InterceptedService::new(reflection_service, move |r| check_auth(r, reflection_service_auth.clone()));
    let (mut health_reporter, health_service) = tonic_health::server::health_reporter();
    health_reporter.set_serving::<RfkposServer<RFKPOSImpl>>().await;
    tokio::spawn(rfkpos_service_status(health_reporter.clone()));

    println!("RFKPOS server listening on {}", addr);

    Server::builder()
        .tls_config(ServerTlsConfig::new().identity(identity))?
        .add_service(health_service)
        .add_service(rfkpos_svc)
        .add_service(reflection_service_svc) // Add this
        .serve(addr)
        .await?;

    Ok(())
}

fn check_auth(req: Request<()>, authenticator: Arc<Mutex<Authenticator>>) -> Result<Request<()>, Status> {
    let token: MetadataValue<_> = authenticator.lock().unwrap().get_token().parse().unwrap();
    let req_token = req.metadata().get("authorization");
    println!("DEBUG: authorization token - {:?}", req_token);
    match req.metadata().get("authorization") {
        Some(t) if token == t => Ok(req),
        _ => Err(Status::unauthenticated("No malid auth token")),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serialize_test() {
        let mut trans = Transaction::default();
        trans.tid = 1;
        trans.parent = 2;
        trans.ts = 20;
        let mut purchase = Purchase::default();
        purchase.ts = Some(Timestamp { year: 2022, month: 12, day: 1, hour: 10, minute: 2, second: 3, tz: 3600 });
        trans.transaction_type = Some(transaction::TransactionType::Purchase(purchase));
        trans.signature = Some("test".to_string());
        let sig = trans.signature;
        trans.signature = None;
        let mut buf = vec![];
        let res = trans.encode(&mut buf);
        println!("DEBUG: {:?} {:?} {:?} {:?}", trans, sig, buf, res);
    }
}