use proc_macro::TokenStream;

mod parse;
mod gen;
mod llm;
mod tunnel;
mod bind_loops;
mod json_store;
mod unified_params;
mod json_include;
mod env_schema;
mod http_response;
mod mad_error;
mod i18n;
mod persist_enum;
mod format_struct;
mod from_row;
mod transaction;
mod main_entry;


/// Bind a user-defined trait to HTTP routes.
///
/// Bind a user-defined trait to HTTP routes — does **not** generate a trait.
/// The trait is hand-written by the user as a pure business interface
/// with zero framework dependencies. `bind_http!` generates axum
/// handlers and a router that delegate to the trait implementation.
///
/// ```ignore
/// // User-written trait (no Mad Hatter dependency)
/// trait UserService {
///     async fn get_user(&self, id: u64) -> User;
///     async fn create_user(&self, body: CreateUserReq) -> User;
///     async fn delete_user(&self, id: u64);
/// }
///
/// impl UserService for App { ... }
///
/// // Bind trait methods to HTTP routes
/// bind_http! {
///     UserService for App {
///         get_user(id: u64)              => GET    "users/{id}" -> Json<User>;
///         create_user(body: CreateUserReq) => POST   "users"     -> Json<User>;
///         delete_user(id: u64)            => DELETE "users/{id}";
///     }
/// }
/// ```
#[proc_macro]
pub fn bind_http(input: TokenStream) -> TokenStream {
    let definition = syn::parse_macro_input!(input as parse::BindDef);
    gen::generate_bind(definition).into()
}

/// Declare HTTP server-level configuration.
///
/// Generates a struct with config constants, embedded page fallback,
/// and authentication middleware. Use with `bind_http!` groups for
/// route declarations.
///
/// ```ignore
/// bind_server! {
///     AliceServer, Arc<EngineState> {
///         auth_policy: EngineState,
///         session_cookie_prefix: "alice_session_",
///         public_dir: "apps/",
///         auth_paths: {
///             login: "/login",
///             root: "/",
///         },
///         embedded_pages: {
///             "/" => "index.html",
///             "/login.html" => "login.html",
///         },
///         body_limit: 200 * BYTES_PER_MB,
///     }
/// }
/// ```
#[proc_macro]
pub fn bind_server(input: TokenStream) -> TokenStream {
    let definition = syn::parse_macro_input!(input as parse::ServerDef);
    gen::generate_server(definition).into()
}

/// Declare loop/cron/batch handler bindings.
///
/// Symmetric to `bind_http!`: module-level declaration with handler types.
/// Main only calls `start()` — individual handler names don't appear in main.
///
/// ```ignore
/// bind_loops! {
///     EngineLoops, Arc<EngineState> {
///         "beat" [batch, refresh 30s] => BeatHandler : BeatCycleFatal,
///         "capture" [batch, cron 60s, refresh 30s] => CaptureHandler : CaptureCycleFatal,
///         "hot-scan" => HotScanLoop : HotScanFatal,
///     }
/// }
/// ```
#[proc_macro]
pub fn bind_loops(input: TokenStream) -> TokenStream {
    let definition = syn::parse_macro_input!(input as bind_loops::LoopsDef);
    bind_loops::generate(definition).into()
}


/// Derive `Promptable` for a struct or enum (new framework).
///
/// Generates `impl Reflectable` + `impl Promptable`.
/// Use `#[prompt("...")]` for field/variant hints (replaces `@render` doc comments).
///
/// ```ignore
/// #[derive(Promptable)]
/// struct BeatRequest {
///     #[prompt("知识")]
///     knowledge: String,
///     #[markdown(skip)]
///     internal: String,
/// }
/// ```
#[proc_macro_derive(Promptable, attributes(markdown, prompt, discriminate, derived))]
pub fn derive_promptable(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    llm::derive_promptable(input).into()
}

/// Derive `Parseable` for an enum or struct (new framework).
///
/// Generates `impl Reflectable` + `impl Promptable` (schema) + `impl Parseable` (parse).
/// Use `#[prompt("...")]` for hints and `#[discriminate(PATH)]` for sealed value matching.
///
/// ```ignore
/// #[derive(Parseable)]
/// enum Action {
///     #[prompt("执行一段脚本")]
///     Script {
///         #[prompt("脚本内容")]
///         content: String,
///     },
///     #[prompt("寄出信件")]
///     SendMsg {
///         #[prompt("收件人填{}代表发给你的专属用户", SENDER_USER)]
///         #[discriminate(crate::bindings::db::SENDER_USER)]
///         recipient: Discriminated<String>,
///     },
/// }
/// ```
#[proc_macro_derive(Parseable, attributes(markdown, prompt, discriminate, parse, derived))]
pub fn derive_parseable(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    llm::derive_parseable(input).into()
}

/// Mark a trait as a tunnel service for RPC over bidirectional transport.
///
/// Generates `{TraitName}Proxy` (remote caller) and `{TraitName}Dispatcher` (local handler).
///
/// All methods must have `&self` receiver and return `Result<T, String>`.
/// Parameters and return types must implement `Serialize + Deserialize`.
///
/// ```ignore
/// #[tunnel_service]
/// trait InstanceComm: Send + Sync {
///     fn relay_message(&self, from: String, to: String, content: String) -> Result<(), String>;
///     fn fetch_contacts(&self) -> Result<Vec<ContactInfo>, String>;
/// }
/// ```
#[proc_macro_attribute]
pub fn tunnel_service(attr: TokenStream, input: TokenStream) -> TokenStream {
    tunnel::derive_tunnel_service(attr, input)
}

/// Declare a JSON-file-backed settings store.
///
/// Generates a struct with all Option fields, plus load/save/merge_fallback
/// and typed `xxx_or_default()` accessors for fields with declared defaults.
///
/// ```ignore
/// json_store!(Settings, "settings.json" {
///     api_key: Option<String>,
///     temperature: Option<f64> = 0.7,
///     model: Option<String> = "gpt-4o",
/// });
/// ```
#[proc_macro]
pub fn json_store(input: TokenStream) -> TokenStream {
    let def = syn::parse_macro_input!(input as json_store::JsonStoreDef);
    json_store::generate(def).into()
}

/// Declare an environment variable schema with strong validation.
///
/// Generates a struct with typed fields and a `from_env()` constructor
/// that reads, validates, and collects all errors before panicking.
///
/// ```ignore
/// unified_params!(Name, "PREFIX", "file.json" {
///     #[required]
///     port: u16,
///     #[readonly]
///     secret: String = "default",
///     timeout_secs: u64 = 300,
/// });
#[proc_macro]
pub fn unified_params(input: TokenStream) -> TokenStream {
    let def = syn::parse_macro_input!(input as unified_params::UnifiedParamsDef);
    unified_params::expand(def).into()
}

/// env_schema!(EnvConfig, "ALICE" {
///     auth_secret: String,              // required: ALICE_AUTH_SECRET
///     http_port: u16,                   // required, parsed: ALICE_HTTP_PORT
///     base_dir: Option<String>,         // optional: ALICE_BASE_DIR
///     host: String = "0.0.0.0",         // default: ALICE_HOST
///     hub_enabled: Option<bool>,        // optional bool: ALICE_HUB_ENABLED
///     #[env("ALICE_API_KEY")]           // custom env name
///     default_api_key: String,
/// });
/// ```
#[proc_macro]
pub fn env_schema(input: TokenStream) -> TokenStream {
    let def = syn::parse_macro_input!(input as env_schema::EnvSchemaDef);
    env_schema::expand(def).into()
}

/// Derive `HttpResponse` for a struct.
///
/// Generates JSON serialization that automatically unwraps `Sealed<T>` fields.
/// Also generates an `IntoResponse` impl so the type can be used directly
/// as an axum handler return value.
///
/// ```ignore
/// #[derive(HttpResponse)]
/// struct CreateResult {
///     id: String,
///     message: Sealed<String>,  // automatically unwrapped
/// }
/// ```
#[proc_macro_derive(HttpResponse)]
pub fn derive_http_response(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    http_response::derive_http_response(input).into()
}

/// Derive `MadError` for an enum.
///
/// Each variant must have a `#[humanize(fn_name(args...))]` attribute
/// that specifies a function returning `Sealed<String>`.
///
/// Generates:
/// - `Display` impl (calls humanize function, unwraps via `value()`)
/// - `Error` impl
/// - `humanize(&self) -> Sealed<String>` method
///
/// ```ignore
/// #[derive(Debug, MadError)]
/// pub enum AppError {
///     #[humanize(msg_not_found(id))]
///     NotFound { id: String },
///
///     #[humanize(msg_unauthorized())]
///     Unauthorized,
/// }
/// ```
#[proc_macro_derive(MadError, attributes(humanize))]
pub fn derive_mad_error(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    mad_error::derive_mad_error(input).into()
}

/// Declare an internationalized message catalog.
///
/// All entries generate `pub fn name(args) -> Sealed<String>`.
///
/// Two entry forms:
/// - **Template**: `name(arg: &str) = "message {arg}";`
/// - **Block**: `name(arg: bool) = { if arg { "yes".into() } else { "no".into() } };`
///
/// Template entries validate placeholders against argument names at compile time.
/// Block entries must evaluate to `String`.
///
/// ```ignore
/// mad_hatter::i18n! {
///     greeting(name: &str) = "Hello, {name}!";
///     complex(flag: bool) = {
///         if flag { "enabled".to_string() } else { "disabled".to_string() }
///     };
/// }
/// ```
#[proc_macro]
pub fn i18n(input: TokenStream) -> TokenStream {
    i18n::i18n_macro(input.into()).into()
}

/// Mark a function as an i18n message producer.
///
/// Compile-time check: the function must return a type containing `Sealed`.
/// Does not alter runtime behavior.
///
/// ```ignore
/// #[i18n]
/// fn describe_change(old: &Settings, new: &Settings) -> Option<Sealed<String>> {
///     // ...
/// }
/// ```
#[proc_macro_attribute]
pub fn i18n_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
    i18n::i18n_attr(_attr.into(), item.into()).into()
}

/// Mark a function as constructing sealed values.
///
/// Pure identity attribute — the function body is unchanged.
/// Guardian exempts literals inside `#[sealed_fn]`-annotated functions,
/// similar to `#[i18n_fn]` but for non-i18n sealed value construction.
#[proc_macro_attribute]
pub fn sealed_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
    item
}

/// Derive `JsonInclude` for a struct.
///
/// Reads a JSON file at compile time and generates construction code.
/// Fields marked `#[from_file]` have their JSON string values replaced
/// with `include_str!` calls (compile-time file embedding).
///
/// ```ignore
/// #[derive(JsonInclude)]
/// #[json_include("manifest.json", array, base = "data/")]
/// pub struct Item {
///     pub title: String,
///     #[from_file]
///     pub content: String,
/// }
///
/// // Generates: Item::load_all() -> Vec<Item>
/// ```
#[proc_macro_derive(JsonInclude, attributes(json_include, from_file))]
pub fn derive_json_include(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    json_include::derive_json_include(input).into()
}

/// Derive `PersistEnum` for enums — unified string mapping for serde + sqlx.
///
/// Generates: `as_str()`, `Display`, `Serialize`, `Deserialize`,
/// and sqlx `Type`/`Encode`/`Decode` impls.
///
/// ```ignore
/// #[derive(PersistEnum)]
/// #[persist_enum(rename_all = "snake_case")]
/// pub enum Role {
///     User,    // -> "user"
///     Agent,   // -> "agent"
///     System,  // -> "system"
/// }
///
/// #[derive(PersistEnum)]
/// #[persist_enum(rename_all = "snake_case")]
/// pub enum ActionType {
///     Idle,
///     ReadMsg,
///     #[persist_enum(other)]
///     Other(String),  // unknown value fallback
/// }
/// ```
#[proc_macro_derive(FormatStruct, attributes(format))]
pub fn derive_format_struct(input: TokenStream) -> TokenStream {
    format_struct::derive(input)
}

#[proc_macro_derive(FormatEnum, attributes(format_enum))]
pub fn derive_format_enum(input: TokenStream) -> TokenStream {
    persist_enum::derive(input)
}

/// Backward-compatible alias for `FormatEnum`.
#[proc_macro_derive(PersistEnum, attributes(persist_enum))]
pub fn derive_persist_enum(input: TokenStream) -> TokenStream {
    persist_enum::derive(input)
}

/// Derive `FromRow` for structs — simplified replacement for `sqlx::FromRow`.
/// Generates paths through `::mad_hatter::__sqlx::` so engine crates
/// don't need sqlx as a direct dependency.
#[proc_macro_derive(FromRow)]
pub fn derive_from_row(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    match from_row::expand_derive_from_row(&input) {
        Ok(output) => output.into(),
        Err(err) => err.to_compile_error().into(),
    }
}

/// Mark a function as a transaction boundary.
///
/// Validates that the transaction name exists in concept-map.json at compile time.
/// Serves as a marker for Guardian's unguarded_io detection.
///
/// ```ignore
/// #[transaction(beat_cycle)]
/// async fn beat(&mut self) -> Result<(), BeatCycleError> {
///     // IO operations must use io!/accept!/suppress! macros
/// }
/// ```
#[proc_macro_attribute]
pub fn transaction(attr: TokenStream, item: TokenStream) -> TokenStream {
    let attr = syn::parse_macro_input!(attr as transaction::TransactionAttr);
    transaction::expand_transaction_attr(attr, item.into()).into()
}

/// Program entry point with startup transaction scope.
///
/// Replaces `fn main()` with a fully managed startup transaction:
/// - Creates a `TxScope<F>` automatically
/// - Wraps the body in `catch_unwind` for fatal abort detection
/// - Exits with code 1 on startup failure
/// - Injects `start_service` as a local binding for launching top-level services
///
/// ```ignore
/// #[mad_hatter::main(AppStartupFatal)]
/// async fn main(scope: &TxScope<AppStartupFatal>) {
///     let db = Db::open(...).propagate(scope, AppStartupFatal::DbInit);
///     let handle = start_service("engine", |c| engine.run(c));
///     handle.join().await;
/// }
/// ```
#[proc_macro_attribute]
pub fn main(attr: TokenStream, input: TokenStream) -> TokenStream {
    main_entry::expand_main(attr.into(), input.into()).into()
}
