#![doc = include_str!("../README.md")]

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{quote, quote_spanned};
use syn::parse::Parse;
use syn::{LitStr, parse_macro_input};

/// Compile-time styling for terminal output using HTML-like syntax.
///
/// This macro processes styling markup at compile time and returns a `String`
/// with the styled text, including ANSI escape sequences.
///
/// # Arguments
///
/// The macro takes a single string literal argument containing HTML-style markup.
///
/// # Returns
///
/// A `String` containing the input text with ANSI escape codes for styling.
///
/// *See the [`crate`] documentation for detailed syntax.*
///
/// # Examples
///
/// Basic foreground color:
/// ```rust
/// # use clye_proc as clyle;
/// use clyle::clyle;
/// let red_text = clyle!("<fg=red>Error</>");
/// ```
///
/// Multiple effects:
/// ```rust
/// # use clye_proc as clyle;
/// use clyle::clyle;
/// let styled = clyle!("<fg=blue bold under>Important</>");
/// ```
///
/// With background:
/// ```rust
/// # use clye_proc as clyle;
/// use clyle::clyle;
/// let highlighted = clyle!("<fg=black bg=yellow>Warning</>");
/// ```
///
/// RGB colors:
/// ```rust
/// # use clye_proc as clyle;
/// use clyle::clyle;
/// let custom = clyle!("<fg=#FF8800>Custom orange</>");
/// ```
///
/// # Compile-Time Processing
///
/// This macro processes the styling at compile time. If there are any syntax errors
/// in the markup, compilation will fail with a descriptive error message pointing
/// to the problem location.
///
/// # Performance
///
/// Unlike runtime parsing (via `try_clyle`), this macro has **zero runtime overhead**:
/// - Styling is processed during compilation
/// - The resulting binary contains only the processed string with ANSI codes
/// - No parsing or validation happens at runtime
///
/// # Errors
///
/// Compilation fails if:
/// - The input is not a string literal
/// - The markup contains invalid color or effect names
/// - Tags are improperly nested or closed
///
#[proc_macro]
pub fn clyle(tokens: TokenStream) -> TokenStream {
    let source = parse_macro_input!(tokens as LitStr);
    let span = source.span();
    let parsed = clyle_core::clyle(&source.value());

    let expanded = quote_spanned!(span=> #parsed);

    TokenStream::from(expanded)
}

struct FormatArgs {
    s: LitStr,
    rest: proc_macro2::TokenStream,
}

impl Parse for FormatArgs {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        Ok(FormatArgs {
            s: input.parse()?,
            rest: input.parse()?,
        })
    }
}

fn impl_fmt(name: &str, tokens: TokenStream) -> TokenStream {
    let args = parse_macro_input!(tokens as FormatArgs);
    let parsed = clyle_core::clyle(&args.s.value());
    let name = Ident::new(name, Span::call_site());
    let s = LitStr::new(&parsed, args.s.span());
    let rest = args.rest;

    let expanded = quote!(::std::#name!(#s #rest));

    TokenStream::from(expanded)
}

/// Format a styled string with arguments at compile time.
/// Combines [`clyle!`] styling with [`format!`] functionality.
#[proc_macro]
pub fn format(tokens: TokenStream) -> TokenStream {
    impl_fmt("format", tokens)
}

/// Print a styled string to stdout at compile time.
/// Combines [`clyle!`] styling with [`print!`] functionality.
#[proc_macro]
pub fn print(tokens: TokenStream) -> TokenStream {
    impl_fmt("print", tokens)
}

/// Print a styled string to stdout with newline at compile time.
/// Combines [`clyle!`] styling with [`println!`] functionality.
#[proc_macro]
pub fn println(tokens: TokenStream) -> TokenStream {
    impl_fmt("println", tokens)
}

/// Print a styled string to stderr at compile time.
/// Combines [`clyle!`] styling with [`eprint!`] functionality.
#[proc_macro]
pub fn eprint(tokens: TokenStream) -> TokenStream {
    impl_fmt("eprint", tokens)
}

/// Print a styled string to stderr with newline at compile time.
/// Combines [`clyle!`] styling with [`eprintln!`] functionality.
#[proc_macro]
pub fn eprintln(tokens: TokenStream) -> TokenStream {
    impl_fmt("eprintln", tokens)
}

/// Write a styled string to a writer at compile time.
/// Combines [`clyle!`] styling with [`write!`] functionality.
#[proc_macro]
pub fn write(tokens: TokenStream) -> TokenStream {
    impl_fmt("write", tokens)
}

/// Write a styled string to a writer with newline at compile time.
/// Combines [`clyle!`] styling with [`writeln!`] functionality.
#[proc_macro]
pub fn writeln(tokens: TokenStream) -> TokenStream {
    impl_fmt("writeln", tokens)
}
