#[cfg(feature = "node")]
use napi_derive::napi;
use serde::{Deserialize, Serialize};

// ============================================================================
// Data Structures
// ============================================================================

/// Result of transforming TypeScript code through the macro system.
///
/// This struct is returned by [`transform_sync`] and contains the transformed code
/// along with optional source maps, type declarations, and metadata about processed classes.
///
/// # Fields
///
/// * `code` - The transformed TypeScript/JavaScript code with macros expanded
/// * `map` - Optional source map for debugging (currently not implemented)
/// * `types` - Optional TypeScript type declarations for generated methods
/// * `metadata` - Optional JSON metadata about processed classes
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
pub struct TransformResult {
    /// The transformed TypeScript/JavaScript code with all macros expanded.
    pub code: String,
    /// Source map for mapping transformed positions back to original.
    /// Currently always `None` - source mapping is handled separately via `SourceMappingResult`.
    pub map: Option<String>,
    /// TypeScript type declarations (`.d.ts` content) for generated methods.
    /// Used by IDEs to provide type information for macro-generated code.
    pub types: Option<String>,
    /// JSON-serialized metadata about processed classes.
    /// Contains information about which classes were processed and what was generated.
    pub metadata: Option<String>,
}

/// A diagnostic message produced during macro expansion.
///
/// Diagnostics can represent errors, warnings, or informational messages
/// that occurred during the macro expansion process.
///
/// # Fields
///
/// * `level` - Severity level: "error", "warning", "info"
/// * `message` - Human-readable description of the issue
/// * `start` - Optional byte offset where the issue starts in the source
/// * `end` - Optional byte offset where the issue ends in the source
///
/// # Example
///
/// ```rust
/// use macroforge_ts::MacroDiagnostic;
///
/// let _diag = MacroDiagnostic {
///     level: "error".to_string(),
///     message: "Unknown macro 'Foo'".to_string(),
///     start: Some(42),
///     end: Some(45),
/// };
/// ```
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MacroDiagnostic {
    /// Severity level of the diagnostic.
    /// One of: "error", "warning", "info".
    pub level: String,
    /// Human-readable message describing the diagnostic.
    pub message: String,
    /// Byte offset in the original source where the issue starts.
    /// `None` if the diagnostic is not associated with a specific location.
    pub start: Option<u32>,
    /// Byte offset in the original source where the issue ends.
    /// `None` if the diagnostic is not associated with a specific location.
    pub end: Option<u32>,
}

/// A segment mapping a range in the original source to a range in the expanded source.
///
/// These segments form the core of the bidirectional source mapping system,
/// enabling IDE features like "go to definition" and error reporting to work
/// correctly with macro-expanded code.
///
/// # Invariants
///
/// - `original_start < original_end`
/// - `expanded_start < expanded_end`
/// - Segments are non-overlapping and sorted by position
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MappingSegmentResult {
    /// Byte offset where this segment starts in the original source.
    pub original_start: u32,
    /// Byte offset where this segment ends in the original source.
    pub original_end: u32,
    /// Byte offset where this segment starts in the expanded source.
    pub expanded_start: u32,
    /// Byte offset where this segment ends in the expanded source.
    pub expanded_end: u32,
}

/// A region in the expanded source that was generated by a macro.
///
/// These regions identify code that has no corresponding location in the
/// original source because it was synthesized by a macro.
///
/// # Example
///
/// For a `@derive(Debug)` macro that generates a `toString()` method,
/// a `GeneratedRegionResult` would mark the entire method body as generated
/// with `source_macro = "Debug"`.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeneratedRegionResult {
    /// Byte offset where the generated region starts in the expanded source.
    pub start: u32,
    /// Byte offset where the generated region ends in the expanded source.
    pub end: u32,
    /// Name of the macro that generated this region (e.g., "Debug", "Clone").
    pub source_macro: String,
}

/// Complete source mapping information for a macro expansion.
///
/// Contains both preserved segments (original code that wasn't modified)
/// and generated regions (new code synthesized by macros).
///
/// # Usage
///
/// This mapping enables:
/// - Converting positions from original source to expanded source and vice versa
/// - Identifying which macro generated a given piece of code
/// - Mapping IDE diagnostics from expanded code back to original source
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SourceMappingResult {
    /// Segments mapping preserved regions between original and expanded source.
    /// Sorted by position for efficient binary search lookups.
    pub segments: Vec<MappingSegmentResult>,
    /// Regions in the expanded source that were generated by macros.
    /// Used to identify synthetic code with no original source location.
    pub generated_regions: Vec<GeneratedRegionResult>,
}

/// Result of expanding macros in TypeScript source code.
///
/// This is the primary return type for macro expansion operations,
/// containing the expanded code, diagnostics, and source mapping.
///
/// # Example
///
/// ```rust
/// use macroforge_ts::{ExpandResult, MacroDiagnostic};
///
/// // Create an ExpandResult programmatically
/// let result = ExpandResult {
///     code: "class User {}".to_string(),
///     types: None,
///     metadata: None,
///     diagnostics: vec![],
///     source_mapping: None,
///     buildtime_dependencies: vec![],
/// };
///
/// // Check for errors
/// if result.diagnostics.iter().any(|d| d.level == "error") {
///     // Handle errors
/// }
/// ```
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExpandResult {
    /// The expanded TypeScript code with all macros processed.
    pub code: String,
    /// Optional TypeScript type declarations for generated methods.
    pub types: Option<String>,
    /// Optional JSON metadata about processed classes.
    pub metadata: Option<String>,
    /// Diagnostics (errors, warnings, info) from the expansion process.
    pub diagnostics: Vec<MacroDiagnostic>,
    /// Source mapping for position translation between original and expanded code.
    pub source_mapping: Option<SourceMappingResult>,
    /// Absolute paths of every file read by `@buildtime` declarations
    /// during evaluation. Consumers (the Vite plugin, loopshot's dev
    /// server, the on-disk cache) use this list for incremental rebuilds:
    /// when any dependency changes, the cache entry for this source
    /// must be invalidated and the file re-expanded.
    pub buildtime_dependencies: Vec<String>,
}

impl ExpandResult {
    /// Creates a no-op result with the original code unchanged.
    ///
    /// Used for early bailout when no macros need processing, avoiding
    /// unnecessary parsing and expansion overhead.
    ///
    /// # Arguments
    ///
    /// * `code` - The original source code to return unchanged
    ///
    /// # Returns
    ///
    /// An `ExpandResult` with:
    /// - `code` set to the input
    /// - All other fields empty/None
    /// - No source mapping (identity mapping implied)
    pub fn unchanged(code: &str) -> Self {
        Self {
            code: code.to_string(),
            types: None,
            metadata: None,
            diagnostics: vec![],
            source_mapping: None,
            buildtime_dependencies: vec![],
        }
    }
}

/// Information about an imported identifier from a TypeScript module.
///
/// Used to track where decorators and macro-related imports come from.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
pub struct ImportSourceResult {
    /// Local identifier name in the import statement (e.g., `Derive` in `import { Derive }`).
    pub local: String,
    /// Module specifier this identifier was imported from (e.g., `"macroforge-ts"`).
    pub module: String,
}

/// Result of checking TypeScript syntax validity.
///
/// Returned by [`check_syntax`] to indicate whether code parses successfully.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
pub struct SyntaxCheckResult {
    /// `true` if the code parsed without errors, `false` otherwise.
    pub ok: bool,
    /// Error message if parsing failed, `None` if successful.
    pub error: Option<String>,
}

/// A span (range) in source code, represented as start position and length.
///
/// Used for mapping diagnostics and other positional information.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
pub struct SpanResult {
    /// Byte offset where the span starts.
    pub start: u32,
    /// Length of the span in bytes.
    pub length: u32,
}

/// A diagnostic from the TypeScript/JavaScript compiler or IDE.
///
/// This structure mirrors TypeScript's diagnostic format for interoperability
/// with language servers and IDEs.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
pub struct JsDiagnostic {
    /// Byte offset where the diagnostic starts. `None` for global diagnostics.
    pub start: Option<u32>,
    /// Length of the diagnostic span in bytes.
    pub length: Option<u32>,
    /// Human-readable diagnostic message.
    pub message: Option<String>,
    /// TypeScript diagnostic code (e.g., 2304 for "Cannot find name").
    pub code: Option<u32>,
    /// Diagnostic category: "error", "warning", "suggestion", "message".
    pub category: Option<String>,
}

/// Options for macro expansion.
///
/// Used by [`expand_sync`] to configure expansion behavior.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExpandOptions {
    /// If `true`, preserves `@derive` decorators in the output.
    /// If `false` (default), decorators are stripped after expansion.
    pub keep_decorators: Option<bool>,

    /// Additional decorator module names from external macros.
    ///
    /// These are used during decorator stripping to identify Macroforge-specific
    /// decorators that should be removed from the output. Built-in decorator modules
    /// (like "serde", "debug") are automatically included.
    ///
    /// External macro packages should export their decorator module names, which
    /// plugins can collect and pass here.
    ///
    /// # Example
    ///
    /// ```javascript
    /// expandSync(code, filepath, {
    ///   keepDecorators: false,
    ///   externalDecoratorModules: ["myMacro", "customValidator"]
    /// });
    /// ```
    pub external_decorator_modules: Option<Vec<String>>,

    /// Path to a previously loaded config file.
    ///
    /// When provided, the expansion will use the cached configuration
    /// (including foreign types) from this path. The config must have been
    /// previously loaded via [`load_config`].
    ///
    /// # Example
    ///
    /// ```javascript
    /// // First, load the config
    /// const configResult = loadConfig(configContent, configPath);
    ///
    /// // Then use it during expansion
    /// expandSync(code, filepath, { configPath });
    /// ```
    pub config_path: Option<String>,

    /// Pre-built type registry JSON from [`scan_project_sync`].
    ///
    /// When provided, macros receive project-wide type awareness through
    /// the `type_registry` and `resolved_fields` fields on `MacroContextIR`.
    ///
    /// # Example
    ///
    /// ```javascript
    /// const scan = scanProjectSync(projectRoot);
    /// expandSync(code, filepath, { typeRegistryJson: scan.registryJson });
    /// ```
    pub type_registry_json: Option<String>,

    /// Pre-built project-wide declarative macro registry JSON from
    /// [`scan_project_sync`]. Built during the same file-tree walk as
    /// `type_registry_json` — no extra I/O is needed to produce it.
    ///
    /// When provided, the declarative macro pre-pass resolves
    /// `/** import macro { $vec, $sum } from "./macros" */` comments by
    /// looking up the target file in this registry. Without it,
    /// cross-file macro imports are unresolved and the pre-pass emits
    /// a diagnostic at each unresolved call site.
    ///
    /// # Example
    ///
    /// ```javascript
    /// const scan = scanProjectSync(projectRoot);
    /// expandSync(code, filepath, {
    ///   typeRegistryJson: scan.registryJson,
    ///   declarativeRegistryJson: scan.declarativeRegistryJson,
    /// });
    /// ```
    pub declarative_registry_json: Option<String>,

    /// Build mode — `"dev"` or `"prod"`. Controls how
    /// reverse-monomorphization declarative macros emit.
    ///
    /// In `dev`, all modes (including `Auto`) behave like `ExpandOnly`
    /// for precise diagnostics and type-checking. In `prod`, `ShareOnly`
    /// and `ShareAnyway` emit a shared runtime helper once per file, and
    /// `Auto` consults the megamorphism analyzer to pick share vs.
    /// cluster vs. expand.
    ///
    /// Defaults to `"dev"` when absent. The Vite plugin wires this from
    /// `config.command` — `serve` → `"dev"`, `build` → `"prod"`.
    pub build_mode: Option<String>,
}

/// Options for processing a file through the macro system.
///
/// Used by [`NativePlugin::process_file`] to configure expansion behavior
/// and caching.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProcessFileOptions {
    /// If `true`, preserves `@derive` decorators in the output.
    /// If `false` (default), decorators are stripped after expansion.
    pub keep_decorators: Option<bool>,
    /// Version string for cache invalidation.
    /// When provided, cached results are only reused if versions match.
    pub version: Option<String>,
    /// Additional decorator module names from external macros.
    /// See [`ExpandOptions::external_decorator_modules`] for details.
    pub external_decorator_modules: Option<Vec<String>>,
    /// Path to a previously loaded config file (for foreign types lookup).
    /// See [`ExpandOptions::config_path`] for details.
    pub config_path: Option<String>,
    /// Pre-built type registry JSON from [`scan_project_sync`].
    /// See [`ExpandOptions::type_registry_json`] for details.
    pub type_registry_json: Option<String>,
    /// Pre-built declarative macro registry JSON from [`scan_project_sync`].
    /// See [`ExpandOptions::declarative_registry_json`] for details.
    pub declarative_registry_json: Option<String>,
    /// Build mode for reverse-monomorphization. See
    /// [`ExpandOptions::build_mode`] for details.
    pub build_mode: Option<String>,
}

/// Options for scanning a TypeScript project for type information.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanOptions {
    /// File extensions to scan (default: `[".ts", ".tsx"]`).
    pub extensions: Option<Vec<String>>,
    /// Whether to only collect exported types (default: `false`).
    pub exported_only: Option<bool>,
}

/// Result of scanning a project for type information.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanResult {
    /// JSON-serialized [`TypeRegistry`].
    /// Pass this to `expand_sync` via `ExpandOptions.type_registry_json`.
    pub registry_json: String,
    /// JSON-serialized
    /// [`ProjectDeclarativeRegistry`](crate::host::declarative::ProjectDeclarativeRegistry)
    /// produced by the same walk as `registry_json`. Pass to `expand_sync`
    /// via `ExpandOptions.declarative_registry_json`.
    pub declarative_registry_json: String,
    /// Number of files scanned.
    pub files_scanned: u32,
    /// Number of types found.
    pub types_found: u32,
    /// Number of declarative macros found across the project.
    pub declarative_macros_found: u32,
    /// Diagnostics from scanning (e.g., files that failed to parse).
    pub diagnostics: Vec<MacroDiagnostic>,
}

/// Result of loading a macroforge configuration file.
///
/// Returned by [`load_config`] after parsing a `macroforge.config.js/ts` file.
#[cfg_attr(feature = "node", napi(object))]
#[derive(Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LoadConfigResult {
    /// Whether to preserve @derive decorators in the output code.
    pub keep_decorators: bool,
    /// Whether to generate a convenience const for non-class types.
    pub generate_convenience_const: bool,
    /// Whether the config has any foreign type handlers defined.
    pub has_foreign_types: bool,
    /// Number of foreign types configured.
    pub foreign_type_count: u32,
}
