#![feature(slice_pattern)]

pub mod utils;

use core::slice::SlicePattern;
use curl::easy::Easy;
use std::io::Read;
use std::str::from_utf8;
use std::{error::Error, net::SocketAddr};
use utils::QueryParameters;
use warp::{http::Method, hyper::body::Bytes, path::FullPath, Filter};

const BASE: &str = "https://www.bankofmaldives.com.mv/internetbanking/api/";
const ERR: &str = "{ code: 407, message: \"Proxy failed\" }";

fn get_proxied_body(uri: &String, method: &Method, body: &Bytes) -> Result<String, Box<dyn Error>> {
    let mut handle = Easy::new();
    handle.url(format!("{}{}", BASE, uri).as_str())?;

    if method == Method::POST {
        handle.post(true)?;
        handle.post_field_size(body.len() as u64)?;
    }

    let mut buf = Vec::new();
    {
        let mut transfer = handle.transfer();

        if method == Method::POST {
            transfer.read_function(|into| {
                let read = body.as_slice().read(into).unwrap_or(0);
                Ok(read)
            })?;
        }

        transfer.write_function(|data| {
            buf.extend_from_slice(data);
            Ok(data.len())
        })?;

        transfer.perform()?;
    }

    let cloned_buf = buf.clone();
    let s = from_utf8(&cloned_buf)?;

    Ok(s.to_string())
}

fn handler(
    uri: FullPath,
    _: QueryParameters,
    method: Method,
    body: Bytes,
    ip: Option<SocketAddr>,
) -> String {
    let uri = uri.as_str().clone().to_string();
    let ip_str = match ip {
        Some(v) => format!("{}", v),
        None => "unknown".to_string(),
    };

    println!("({}) {}", ip_str, uri);

    let proxy_response = get_proxied_body(&uri, &method, &body);

    let res = match proxy_response {
        Ok(v) => v,
        Err(e) => {
            println!("error: {}", e);
            ERR.to_string()
        }
    };

    return res;
}

#[tokio::main]
async fn main() {
    let proxy_handler = warp::any()
        .and(utils::extract_request_data_filter())
        .and(warp::filters::addr::remote())
        .map(handler);
    warp::serve(proxy_handler).run(([0, 0, 0, 0], 3030)).await;
}
