use runmat_analysis_core::{AnalysisField, AnalysisFieldValues, DeviceFieldRef};
use runmat_analysis_fea::{ComputeBackend, FeaRunResult};

pub(crate) fn promote_run_fields_to_device_refs(
    run: &mut FeaRunResult,
    fallback_events: &mut Vec<String>,
) {
    if run.backend != ComputeBackend::Gpu {
        return;
    }

    for field in &mut run.fields {
        let field_label = field.field_id.clone();
        promote_field_to_device_ref(&field_label, field, fallback_events);
    }
}

fn promote_field_to_device_ref(
    field_label: &str,
    field: &mut AnalysisField,
    fallback_events: &mut Vec<String>,
) {
    let host_values = match &field.values {
        AnalysisFieldValues::HostF64(values) => values.clone(),
        AnalysisFieldValues::DeviceRef(_) => return,
    };

    let Some(provider) = runmat_accelerate_api::provider() else {
        fallback_events.push(format!(
            "BACKEND_NO_PROVIDER:{field_label}:retained_host_field"
        ));
        return;
    };

    let shape = field.shape.clone();
    let view = runmat_accelerate_api::HostTensorView {
        data: &host_values,
        shape: &shape,
    };
    match provider.upload(&view) {
        Ok(handle) => {
            let backend = provider
                .device_info_struct()
                .backend
                .unwrap_or_else(|| "gpu".to_string());
            field.values = AnalysisFieldValues::DeviceRef(DeviceFieldRef {
                backend,
                token: format!("device:{}:buffer:{}", handle.device_id, handle.buffer_id),
                element_count: host_values.len(),
            });
        }
        Err(error) => {
            fallback_events.push(format!("BACKEND_UPLOAD_FAILED:{field_label}:{}", error))
        }
    }
}
