use std::borrow::Cow;

use rolldown_plugin::{HookLoadReturn, HookUsage, Plugin};

// Include the embedded helpers generated by build.rs
mod generated;
use generated::embedded_helpers::{
  RUNTIME_HELPER_PREFIX, RUNTIME_HELPER_UNVERSIONED_PREFIX, get_helper_content, is_runtime_helper,
  is_virtual_runtime_helper,
};

/// Plugin for handling @oxc-project/runtime helpers
/// This plugin embeds all ESM helpers from @oxc-project/runtime directly
/// to support browser environments where module resolution is not available
#[derive(Debug)]
pub struct OxcRuntimePlugin;

impl Plugin for OxcRuntimePlugin {
  fn name(&self) -> Cow<'static, str> {
    Cow::Borrowed("builtin:oxc-runtime")
  }

  fn register_hook_usage(&self) -> HookUsage {
    HookUsage::ResolveId | HookUsage::Load
  }

  async fn resolve_id(
    &self,
    _ctx: &rolldown_plugin::PluginContext,
    args: &rolldown_plugin::HookResolveIdArgs<'_>,
  ) -> rolldown_plugin::HookResolveIdReturn {
    if is_runtime_helper(args.specifier) {
      // Create virtual module ID with \0 prefix and ensure .js suffix
      let virtual_id = rolldown_utils::concat_string!(
        "\0",
        RUNTIME_HELPER_PREFIX,
        &args.specifier[RUNTIME_HELPER_UNVERSIONED_PREFIX.len()..],
        if args.specifier.ends_with(".js") { "" } else { ".js" }
      );
      return Ok(Some(rolldown_plugin::HookResolveIdOutput {
        id: virtual_id.into(),
        ..Default::default()
      }));
    }

    // Handle relative imports within the helpers, e.g., "./typeof.js"
    Ok(args.importer.and_then(|importer| {
      // Check if importer is a virtual module (starts with \0)
      let importer = importer.strip_prefix('\0')?;
      if !is_virtual_runtime_helper(importer) || !args.specifier.starts_with("./") {
        return None;
      }

      // Construct the full path for the relative import
      // e.g., "\0@oxc-project/runtime@VERSION/helpers/typeof.js"
      let full_path = rolldown_utils::concat_string!(
        "\0",
        RUNTIME_HELPER_PREFIX,
        args.specifier[2..].trim_start_matches("./")
      );

      Some(rolldown_plugin::HookResolveIdOutput { id: full_path.into(), ..Default::default() })
    }))
  }

  fn resolve_id_meta(&self) -> Option<rolldown_plugin::PluginHookMeta> {
    Some(rolldown_plugin::PluginHookMeta { order: Some(rolldown_plugin::PluginOrder::Pre) })
  }

  async fn load(
    &self,
    _ctx: rolldown_plugin::SharedLoadPluginContext,
    args: &rolldown_plugin::HookLoadArgs<'_>,
  ) -> HookLoadReturn {
    Ok(args.id.strip_prefix('\0').and_then(|id| {
      get_helper_content(id)
        .map(|code| rolldown_plugin::HookLoadOutput { code, ..Default::default() })
    }))
  }

  fn load_meta(&self) -> Option<rolldown_plugin::PluginHookMeta> {
    Some(rolldown_plugin::PluginHookMeta { order: Some(rolldown_plugin::PluginOrder::Pre) })
  }
}
