use base64::{Engine, engine::general_purpose::URL_SAFE};
use iri_string::types::{UriAbsoluteStr, UriAbsoluteString};

use crate::vscode_sys;

pub fn from_vscode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
    let uri_string = vscode_uri.to_string_skip_encoding(true)?;

    UriAbsoluteString::try_from(uri_string)
        .map_err(|error| napi::Error::from_reason(format!("Failed to parse URI: {error}")))
}

pub fn to_vscode<'env>(
    env: &'env napi::Env,
    uri: &UriAbsoluteStr,
) -> Result<vscode_sys::Uri<'env>, napi::Error> {
    vscode_sys::Uri::parse(env, uri.as_str(), true)
}

pub fn encode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
    let uri_to_encode = vscode_uri.to_string_skip_encoding(true)?;
    let encoded_path = format!("/{}", URL_SAFE.encode(uri_to_encode));

    let mut builder = iri_string::build::Builder::new();
    builder.scheme(crate::PIJUL_SCHEME);
    // TODO: support different hosts with different actions
    builder.host("tracked");
    builder.path(&encoded_path);
    builder.normalize();

    let built_uri = builder
        .build()
        .map_err(|error| napi::Error::from_reason(format!("Failed to build URI: {error}")))?;

    Ok(UriAbsoluteString::from(built_uri))
}

pub fn decode(vscode_uri: &vscode_sys::Uri) -> Result<UriAbsoluteString, napi::Error> {
    // Example: pijul:tracked/dnNjb2RlOmV4YW1wbGUvdXJp
    let pijul_uri = from_vscode(vscode_uri)?;
    // Example: dnNjb2RlOmV4YW1wbGUvdXJp
    let uri_to_decode = pijul_uri.path_str().strip_prefix('/').ok_or_else(|| {
        napi::Error::from_reason(format!("URI path did not contain `/`: {pijul_uri}"))
    })?;

    let decoded_bytes = URL_SAFE.decode(uri_to_decode).map_err(|error| {
        napi::Error::from_reason(format!("Failed to decode base64 bytes: {error}"))
    })?;
    // Example: vscode:example/uri
    let decoded_text = String::from_utf8(decoded_bytes).map_err(|error| {
        napi::Error::from_reason(format!("Failed to parse UTF-8 bytes: {error}"))
    })?;

    UriAbsoluteString::try_from(decoded_text)
        .map_err(|error| napi::Error::from_reason(format!("Failed to parse decoded URI: {error}")))
}