//! TypeScript export tests for the dashboard generated-types pipeline.

use std::fs;
use std::path::PathBuf;

use ts_rs::{Config, TS};

use crate::{
    ActivityError, ActivityErrorKind, ActivityId, CatchUpPolicy, ContentType, Event, EventEnvelope,
    OverlapPolicy, PackageVersion, Payload, RunId, ScheduleConfig, ScheduleId, SearchAttributeType,
    SearchAttributeValue, TimerId, TriggerSpec, WorkflowError, WorkflowFilter, WorkflowId,
    WorkflowStatus, WorkflowSummary,
};

const DASHBOARD_GENERATED_DIR: &str = "../../apps/aion-dashboard/src/types/generated";
const GENERATED_HEADER: &str = concat!(
    "// This file is @generated by the aion-core/aion-proto ts-rs generated-types pipeline.\n",
    "// Do not edit by hand. Regenerate with the command documented in ",
    "apps/aion-dashboard/README.md.\n\n",
);
const TS_RS_HEADER: &str = concat!(
    "// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). ",
    "Do not edit this file manually.\n\n",
);

#[test]
fn export_dashboard_wire_types() -> Result<(), Box<dyn std::error::Error>> {
    let generated = dashboard_wire_types()?;
    let output_path = dashboard_generated_index_path();

    fs::write(output_path, generated)?;

    Ok(())
}

fn dashboard_wire_types() -> Result<String, ts_rs::ExportError> {
    let config = Config::new().with_out_dir(".").with_large_int("number");
    let mut output = String::from(GENERATED_HEADER);

    push_type::<WorkflowId>(&config, &mut output)?;
    push_type::<RunId>(&config, &mut output)?;
    push_type::<PackageVersion>(&config, &mut output)?;
    push_type::<ScheduleId>(&config, &mut output)?;
    push_type::<ActivityId>(&config, &mut output)?;
    push_type::<TimerId>(&config, &mut output)?;
    output.push_str("export type Namespace = string;\n\n");
    push_type::<ContentType>(&config, &mut output)?;
    push_type::<Payload>(&config, &mut output)?;
    push_type::<TriggerSpec>(&config, &mut output)?;
    push_type::<OverlapPolicy>(&config, &mut output)?;
    push_type::<CatchUpPolicy>(&config, &mut output)?;
    push_type::<ScheduleConfig>(&config, &mut output)?;
    push_type::<WorkflowError>(&config, &mut output)?;
    push_type::<ActivityErrorKind>(&config, &mut output)?;
    push_type::<ActivityError>(&config, &mut output)?;
    push_type::<WorkflowStatus>(&config, &mut output)?;
    push_type::<WorkflowFilter>(&config, &mut output)?;
    push_type::<WorkflowSummary>(&config, &mut output)?;
    push_type::<SearchAttributeType>(&config, &mut output)?;
    push_type::<SearchAttributeValue>(&config, &mut output)?;
    push_type::<EventEnvelope>(&config, &mut output)?;
    push_type::<Event>(&config, &mut output)?;

    Ok(output)
}

fn push_type<T>(config: &Config, output: &mut String) -> Result<(), ts_rs::ExportError>
where
    T: TS + 'static,
{
    let declaration = T::export_to_string(config)?;
    output.push_str(strip_ts_rs_prelude(&declaration));
    output.push('\n');
    Ok(())
}

fn strip_ts_rs_prelude(declaration: &str) -> &str {
    let without_header = declaration
        .strip_prefix(TS_RS_HEADER)
        .unwrap_or(declaration);

    without_header
        .find("\nexport ")
        .map_or(without_header, |export_start| {
            &without_header[export_start + 1..]
        })
}

fn dashboard_generated_index_path() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join(DASHBOARD_GENERATED_DIR)
        .join("index.ts")
}
