#![allow(dead_code)]
use std::sync::Arc;

use hamelin_lib::ast::err::ContextualTranslationErrors;
use hamelin_lib::catalog::Catalog;
use hamelin_lib::translation::{ContextualResult, QueryTranslation};
use hamelin_lib::FunctionDescription;
use serde::Serialize;
use tsify_next::Tsify;
use wasm_bindgen::prelude::*;

pub const VERSION: &str = env!("CARGO_PKG_VERSION");

#[wasm_bindgen]
struct CatalogProvider {
    wrapped: Arc<hamelin_lib::catalog::CatalogProvider>,
}

#[wasm_bindgen]
impl CatalogProvider {
    pub fn try_from_catalog(catalog: Catalog) -> Result<Self, JsValue> {
        Ok(Self {
            wrapped: Arc::new(
                hamelin_lib::catalog::CatalogProvider::try_from(catalog)
                    .map_err(|e| JsValue::from_str(e.to_string().as_str()))?,
            ),
        })
    }
}

#[wasm_bindgen]
struct Compiler {
    wrapped: hamelin_lib::Compiler,
}

#[wasm_bindgen]
impl Compiler {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self {
            wrapped: hamelin_lib::Compiler::new(),
        }
    }

    pub fn get_function_descriptions(&self) -> Vec<FunctionDescription> {
        self.wrapped.get_function_descriptions()
    }

    pub fn set_catalog_provider(&mut self, provider: CatalogProvider) {
        self.wrapped.set_environment_provider(provider.wrapped);
    }

    #[cfg(target_arch = "wasm32")]
    pub fn set_time_range(&mut self, start: Option<js_sys::Date>, end: Option<js_sys::Date>) {
        use chrono::{DateTime, Utc};
        use hamelin_lib::TimeRange;

        self.wrapped.set_time_range(
            TimeRange {
                start: start.map(|d| DateTime::<Utc>::from(d)),
                end: end.map(|d| DateTime::<Utc>::from(d)),
            }
            .into(),
        );
    }

    pub fn set_time_range_expression(
        &mut self,
        expression: String,
    ) -> Option<ContextualTranslationErrors> {
        match self.wrapped.set_time_range_expression(expression) {
            Ok(_) => None,
            Err(e) => Some(e),
        }
    }

    pub fn compile_query(&self, query: String) -> CompileQueryResult {
        let res = self.wrapped.compile_query(query);
        match res {
            Ok(t) => CompileQueryResult::Ok(t),
            Err(e) => CompileQueryResult::Err(e),
        }
    }

    pub fn compile_query_at(&self, query: String, at: Option<usize>) -> ContextualResult {
        self.wrapped.compile_query_at(query, at)
    }

    pub fn get_statement_datasets(&self, query: String) -> QueryDatasetsResult {
        match self.wrapped.get_statement_datasets(query) {
            Ok(datasets) => QueryDatasetsResult::Ok(datasets),
            Err(e) => QueryDatasetsResult::Err(e),
        }
    }
}

#[derive(Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum CompileQueryResult {
    Ok(QueryTranslation),
    Err(ContextualTranslationErrors),
}

#[derive(Serialize, Tsify)]
#[tsify(into_wasm_abi)]
pub enum QueryDatasetsResult {
    Ok(Vec<String>),
    Err(ContextualTranslationErrors),
}
