// r8r: rust port of n8n.io
// Initial author: Michael Cochran <mcochran@cochranblock.org>
// Built for: Tanner Woodrum, intended cofounder
// License: Unlicense (public domain dedication, see UNLICENSE)
//
// `perplexity` — chat against the Perplexity API. Hand-built using
// the routing runtime — first proof point that codegen can replicate
// this shape automatically (extracted from the n8n source's
// `requestDefaults` + property `routing.request` blocks + the
// PerplexityApi credential's `authenticate.headers`).
//
// Credential: perplexity = { apiKey = "..." }

use async_trait::async_trait;
use crate::core::error::NodeError;
use crate::core::types::*;
use crate::engine::routing::{execute_routing, OperationDescriptor, RoutingDescriptor};
use crate::engine::{ExecuteCtx, Node, NodeFactory};
use std::sync::Arc;

pub struct PerplexityNode;

static META: NodeMetadata = NodeMetadata {
    name: "perplexity",
    display_name: "Perplexity",
    version: &[1],
    group: &["ai"],
    description: "Chat against the Perplexity API",
    inputs: 1,
    outputs: 1,
    properties: &[],
    credentials: &[CredentialRef { name: "perplexity", required: true }],
};

static ROUTING: RoutingDescriptor = RoutingDescriptor {
    base_url: "https://api.perplexity.ai",
    credential: Some("perplexity"),
    auth_headers: &[
        ("Authorization", "Bearer {{apiKey}}"),
    ],
    auth_query: &[],
    dispatch_param: "operation",
    operations: &[
        OperationDescriptor {
            value: "chat",
            method: "POST",
            url_template: "/chat/completions",
            body_template: r#"{"model": "{{model}}", "messages": [{"role": "user", "content": "{{prompt}}"}]}"#,
            query: &[],
        },
    ],
    sends: &[],
};

#[async_trait]
impl Node for PerplexityNode {
    fn metadata(&self) -> &'static NodeMetadata { &META }

    async fn execute(&self, ctx: &mut ExecuteCtx<'_>) -> Result<NodeOutput, NodeError> {
        // Allow a base_url override via credential for sims and self-hosted.
        let override_base = ctx.credentials.field("perplexity", "base_url").ok()
            .and_then(|v| v.as_str().map(|s| s.trim_end_matches('/').to_string()));
        if let Some(b) = override_base {
            // Build a fresh descriptor with the override; we leak the String
            // into a 'static via a Box::leak so RoutingDescriptor's &'static str
            // bound is satisfied for this call.
            let leaked: &'static str = Box::leak(b.into_boxed_str());
            let desc = RoutingDescriptor {
                base_url: leaked,
                credential: ROUTING.credential,
                auth_headers: ROUTING.auth_headers,
                auth_query: ROUTING.auth_query,
                dispatch_param: ROUTING.dispatch_param,
                operations: ROUTING.operations,
                sends: ROUTING.sends,
            };
            return execute_routing(&desc, ctx).await;
        }
        execute_routing(&ROUTING, ctx).await
    }
}

inventory::submit! { NodeFactory::new("perplexity", || Arc::new(PerplexityNode)) }
