//! Helper module for the #[dampen_ui] macro.
//!
//! This module contains the implementation details for the dampen_ui macro.
//!
//! # Dual-Mode Architecture Support
//!
//! This module supports two distinct compilation modes:
//!
//! - **Interpreted Mode** (default): Compiles with runtime XML parsing for hot-reload
//! - **Codegen Mode**: Compiles with placeholders expecting build.rs generated code
//!
//! The mode is determined by Cargo feature flags at compile time.

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{ItemMod, LitStr};

/// Process the #[dampen_ui] attribute macro.
///
/// Usage:
/// ```rust,ignore
/// use dampen_macros::dampen_ui;
///
/// #[dampen_ui("app.dampen")]
/// mod app_dampen {}
/// ```
///
/// This generates a module with a `document()` function that returns a cloned DampenDocument.
///
/// # Dual-Mode Architecture
///
/// The macro generates different code based on feature flags:
///
/// ## Interpreted Mode (Default)
///
/// Enabled when:
/// - No features specified (default)
/// - `feature = "interpreted"` is explicitly enabled
/// - Both features are enabled (interpreted takes priority)
///
/// Behavior:
/// - Loads and parses XML at runtime using LazyLock
/// - Supports hot-reload for rapid development iteration
/// - Embeds XML content via `include_str!` at compile time
/// - Parses on first access to `document()` function
///
/// ## Codegen Mode
///
/// Enabled when:
/// - `feature = "codegen"` is enabled
/// - `feature = "interpreted"` is NOT enabled
///
/// Behavior:
/// - Generates placeholder module structure
/// - Expects build.rs to generate actual implementation
/// - Panics with helpful error if build.rs is missing
/// - Zero runtime overhead when properly configured
///
/// # Error Codes
///
/// - **D001**: File not found - The specified .dampen file does not exist
/// - **D002**: Invalid XML - The XML content could not be parsed
/// - **D004**: Parse error - DampenDocument parsing failed
/// - **D005**: Codegen mode missing build.rs - build.rs must generate code in codegen mode
///
/// # Examples
///
/// ## Interpreted Mode
///
/// ```rust,ignore
/// // Cargo.toml: [features] default = ["interpreted"]
///
/// #[dampen_ui("app.dampen")]
/// mod _app {}
///
/// // Use the generated module
/// let document = _app::document(); // Parses XML on first call
/// ```
///
/// ## Codegen Mode
///
/// ```rust,ignore
/// // Cargo.toml: [features] codegen = []
/// // build.rs: Generate code to OUT_DIR/ui_app.rs
///
/// #[dampen_ui("app.dampen")]
/// mod _app {}
///
/// // User includes generated code
/// #[cfg(feature = "codegen")]
/// include!(concat!(env!("OUT_DIR"), "/ui_app.rs"));
/// ```
pub fn process_dampen_ui(attr: TokenStream, item: TokenStream) -> TokenStream {
    // Parse the path from macro attributes
    let file_path = if let Ok(path) = syn::parse::<LitStr>(attr) {
        path.value()
    } else {
        return syn::Error::new(
            Span::call_site(),
            "Expected a string literal for the path argument\n\
             Usage: #[dampen_ui(\"app.dampen\")]",
        )
        .to_compile_error()
        .into();
    };

    // Parse the input as a module
    #[allow(clippy::expect_used)]
    let input = syn::parse::<ItemMod>(item)
        .map_err(|e| syn::Error::new(Span::call_site(), format!("Expected a module item: {}", e)))
        .expect("Failed to parse module");

    let module_ident = &input.ident;
    let vis = &input.vis; // Preserve visibility

    // Generate different code based on feature flags
    // Priority: codegen > interpreted > default (interpreted)
    let expanded = quote! {
        // Codegen mode: Expect generated code from build.rs
        #[cfg(all(feature = "codegen", not(feature = "interpreted")))]
        #vis mod #module_ident {
            // The actual implementation is generated by build.rs and included at compile time
            // build.rs should generate a file with document() function and DOCUMENT static
            // This is a placeholder to satisfy the module structure

            use dampen_core::DampenDocument;
            use std::sync::LazyLock;

            // Note: In codegen mode, build.rs must generate code that provides:
            // - pub static DOCUMENT: LazyLock<DampenDocument>
            // - pub fn document() -> DampenDocument
            // These will be included via include! in the user's code

            // Placeholder implementations (should never be called in codegen mode)
            pub static DOCUMENT: LazyLock<DampenDocument> = LazyLock::new(|| {
                panic!("Codegen mode requires build.rs to generate UI code. \
                       Add a build.rs file that generates code for {}", #file_path)
            });

            pub fn document() -> DampenDocument {
                panic!("Codegen mode requires build.rs to generate UI code. \
                       Add a build.rs file that generates code for {}", #file_path)
            }
        }

        // Interpreted mode: Runtime loading with hot-reload support
        #[cfg(any(feature = "interpreted", not(feature = "codegen")))]
        #vis mod #module_ident {
            use dampen_core::parse;
            use std::sync::LazyLock;

            fn __load_document() -> dampen_core::DampenDocument {
                let xml = include_str!(#file_path);
                #[allow(clippy::expect_used)]
                parse(xml).expect("Failed to parse Dampen UI file")
            }

            pub static DOCUMENT: LazyLock<dampen_core::DampenDocument> =
                LazyLock::new(__load_document);

            pub fn document() -> dampen_core::DampenDocument {
                (*DOCUMENT).clone()
            }
        }
    };

    TokenStream::from(expanded)
}

/// Generate a handler for unknown handler references.
///
/// This can be used at compile time to warn about handlers that
/// are referenced in the UI but not registered.
#[allow(dead_code)]
pub fn warn_unknown_handler(handler_name: &str) -> TokenStream {
    let warning = format!(
        "Handler '{}' is not registered in the HandlerRegistry\n\
         help: Register handlers manually with HandlerRegistry::register_simple() or check for typos",
        handler_name
    );

    quote::quote! {
        compile_error!(#warning);
    }
    .into()
}
