// Auto-generated CLI module - generated by firestone
use clap::{Parser, Subcommand};
use serde_json;
use std::sync::Arc;

use crate::apis::configuration::Configuration;

{% set enum_types = [] -%}
{% for op in rsrc["operations"]["resource"] + rsrc["operations"]["instance"] -%}
{% for attr in op["attrs"] -%}
{% if attr.get("is_enum") and attr["type"] not in enum_types -%}
{% set _ = enum_types.append(attr["type"]) -%}
#[derive(clap::ValueEnum, Clone, Debug)]
enum {{ attr["type"] }} {
{% for variant in attr["enum_variants"] -%}
    {{ variant["name"] }},
{% endfor -%}
}
{% endif -%}
{% endfor -%}
{% endfor -%}

// Context for API client - similar to Python's ctx_obj
pub struct ApiContext {
    pub api_client: Arc<Configuration>,
}

#[derive(Subcommand, Debug)]
pub enum {{ rsrc["pascal_name"] }}Commands {
    {% set comp_name = rsrc["name"] if not rsrc["name"].endswith("s") else rsrc["name"][:-1] -%}
    {# high-level resource operations -#}
    {% for op in rsrc["operations"]["resource"]|sort(attribute='name') -%}
    /// {{ op["description"] }}
    {{ op["pascal_name"] }}({{ op["pascal_name"] }}Args),
    {% endfor -%}

    {# high-level resource instance operations -#}
    {% for op in rsrc["operations"]["instance"]|sort(attribute='name') -%}
    /// {{ op["description"] }}
    {{ op["pascal_name"] }}({{ op["pascal_name"] }}Args),
    {% endfor -%}
}

{# Resource operations -#}
{% for op in rsrc["operations"]["resource"]|sort(attribute='name') -%}
#[derive(Parser, Debug)]
pub struct {{ op["pascal_name"] }}Args {
{% for attr in op["attrs"]|sort(attribute='name') -%}
    /// {{ attr["description"] }}
    {% if attr["type"] == "bool" -%}
    #[arg(long, action = clap::ArgAction::SetTrue)]
    {% elif attr.get("is_enum") -%}
    #[arg(long, value_enum)]
    {% else -%}
    #[arg(long)]
    {% endif -%}
    {% if attr.get("default") is not none -%}
    #[arg(default_value = "{{ attr["default"] }}")]
    {% endif -%}
    {% if not attr["required"] and attr.get("default") is none -%}
    pub {{ attr["rust_name"] }}: Option<{{ attr["type"] }}>,
    {% else -%}
    pub {{ attr["rust_name"] }}: {{ attr["type"] }},
    {% endif -%}
{% endfor -%}
}

{% endfor -%}

{# Instance operations -#}
{% for op in rsrc["operations"]["instance"]|sort(attribute='name') -%}
{# Track seen attribute names to avoid duplicates -#}
{% set seen_names = [] -%}
{% set arg_names = [] -%}
{% for attr in op["attrs"]|sort(attribute='name') -%}
{% if attr.get("argument") -%}
{% set _ = arg_names.append(attr["name"]) -%}
{% endif -%}
{% endfor -%}
#[derive(Parser, Debug)]
pub struct {{ op["pascal_name"] }}Args {
{% for attr in op["attrs"]|sort(attribute='name') -%}
    {% if attr["name"] not in seen_names -%}
    {% set _ = seen_names.append(attr["name"]) -%}
    {% if attr.get("argument") -%}
    /// {{ attr["description"] }}
    pub {{ attr["rust_name"] }}: {{ attr["type"] }},
    {% else -%}
    /// {{ attr["description"] }}
    {% if attr["type"] == "bool" -%}
    #[arg(long, action = clap::ArgAction::SetTrue)]
    {% elif attr.get("is_enum") -%}
    #[arg(long, value_enum)]
    {% else -%}
    #[arg(long)]
    {% endif -%}
    {% if attr.get("default") is not none -%}
    #[arg(default_value = "{{ attr["default"] }}")]
    {% endif -%}
    {% if not attr["required"] and attr.get("default") is none -%}
    pub {{ attr["rust_name"] }}: Option<{{ attr["type"] }}>,
    {% else -%}
    pub {{ attr["rust_name"] }}: {{ attr["type"] }},
    {% endif -%}
    {% endif -%}
    {% endif -%}
{% endfor -%}
}

{% endfor -%}

pub async fn handle_{{ rsrc["name"] }}_command(
    ctx: &ApiContext,
    cmd: &{{ rsrc["pascal_name"] }}Commands,
) -> Result<(), Box<dyn std::error::Error>> {
    match cmd {
{% for op in rsrc["operations"]["resource"]|sort(attribute='name') -%}
        {{ rsrc["pascal_name"] }}Commands::{{ op["pascal_name"] }}(args) => {
            handle_{{ op["id"] }}(ctx, args).await
        },
{% endfor -%}
{% for op in rsrc["operations"]["instance"]|sort(attribute='name') -%}
        {{ rsrc["pascal_name"] }}Commands::{{ op["pascal_name"] }}(args) => {
            handle_{{ op["id"] }}(ctx, args).await
        },
{% endfor -%}
    }
}

{# Resource operation handlers -#}
{% for op in rsrc["operations"]["resource"]|sort(attribute='name') -%}
async fn handle_{{ op["id"] }}(
    ctx: &ApiContext,
    args: &{{ op["pascal_name"] }}Args,
) -> Result<(), Box<dyn std::error::Error>> {
    {% if op["name"] == "create" -%}
    // Build request body for create operation
    {% set limit_attr = None -%}
    {% set offset_attr = None -%}
    {% for attr in op["attrs"]|sort(attribute='name') -%}
    {% if attr["name"] == "limit" -%}
    {% set limit_attr = attr -%}
    {% elif attr["name"] == "offset" -%}
    {% set offset_attr = attr -%}
    {% endif -%}
    {% endfor -%}
    let req_body = crate::models::Create{{ rsrc["comp_name_pascal"] }} {
        {% for attr in op["attrs"]|sort(attribute='name') -%}
        {% if attr["name"] != "limit" and attr["name"] != "offset" -%}
        {{ attr["rust_name"] }}: {% if attr.get("body_conversion") == "ENUM_MATCH" -%}
        {% if attr.get("required") -%}
        match args.{{ attr["rust_name"] }} {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            {{ attr["type"] }}::{{ mapping["cli_variant"] }} => {{ attr.get("enum_model_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
        }
        {%- elif attr.get("default") -%}
        Some(match args.{{ attr["rust_name"] }} {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            {{ attr["type"] }}::{{ mapping["cli_variant"] }} => {{ attr.get("enum_model_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
        })
        {%- else -%}
        args.{{ attr["rust_name"] }}.as_ref().map(|e| match e {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            {{ attr["type"] }}::{{ mapping["cli_variant"] }} => {{ attr.get("enum_model_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
        })
        {%- endif -%}
{% elif attr.get("body_conversion") == "ENUM_ARRAY_MATCH" -%}
        args.{{ attr["rust_name"] }}.iter().map(|s| match s.to_lowercase().as_str() {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            "{{ mapping["cli_variant"]|lower }}" => {{ attr.get("enum_item_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
            _ => panic!("Invalid enum value for {{ attr["name"] }}: {}", s),
        }).collect(){% elif attr.get("body_conversion") and attr.get("body_conversion") != "None" -%}{{ attr["body_conversion"] }}{% else -%}args.{{ attr["rust_name"] }}.clone(){% endif -%},
        {% endif -%}
        {% endfor -%}
    };
    
    // Extract query parameters (limit, offset) if present
    {% if limit_attr -%}
    let limit = args.{{ limit_attr["rust_name"] }}.map(|v| v as i32);
    {% else -%}
    let limit = None::<i32>;
    {% endif -%}
    {% if offset_attr -%}
    let offset = args.{{ offset_attr["rust_name"] }}.map(|v| v as i32);
    {% else -%}
    let offset = None::<i32>;
    {% endif -%}
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, req_body, limit, offset).await?;
    {% else -%}
    // Build query parameters - openapi-generator functions take individual Option parameters
    {% for qp in op.get("query_params", []) -%}
    {% if qp["type"] == "String" -%}
    {% if qp["required"] -%}
    let {{ qp["rust_name"] }}_param = Some(args.{{ qp["rust_name"] }}.as_str());
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }}.as_ref().map(|s| s.as_str());
    {% endif -%}
    {% elif qp["type"] == "i64" -%}
    {% if qp["required"] -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }} as i32;
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }}.map(|v| v as i32);
    {% endif -%}
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }};
    {% endif -%}
    {% endfor -%}
    {% if op["name"] == "delete" -%}
    // DELETE operations may return empty responses (204 No Content)
    // Handle gracefully without panicking on content type mismatches
    let resp_result = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await;
    match resp_result {
        Ok(data) => {
            // If we got JSON data, serialize and print it
            let json_output = serde_json::to_string_pretty(&data)?;
            println!("{}", json_output);
        },
        Err(crate::apis::Error::Serde(e)) => {
            // Check if this is a content type mismatch (empty response)
            let error_msg = e.to_string();
            if error_msg.contains("content type") || error_msg.contains("application/octet-stream") || error_msg.contains("cannot be converted") {
                // Empty response is OK for DELETE - operation succeeded
                // Output success JSON
                println!("{}", r#"{"status": "deleted"}"#);
            } else {
                // Real serialization error, propagate it
                return Err(Box::new(e));
            }
        },
        Err(e) => {
            // Other errors (network, HTTP errors, etc.) - propagate
            return Err(Box::new(e));
        }
    }
    {% else -%}
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await?;
    // Output response as JSON
    let json_output = serde_json::to_string_pretty(&resp)?;
    println!("{}", json_output);
    {% endif -%}
    {% endif -%}
    Ok(())
}

{% endfor -%}

{# Instance operation handlers -#}
{% for op in rsrc["operations"]["instance"]|sort(attribute='name') -%}
async fn handle_{{ op["id"] }}(
    ctx: &ApiContext,
    args: &{{ op["pascal_name"] }}Args,
) -> Result<(), Box<dyn std::error::Error>> {
    {% if op["name"] == "update" -%}
    // Build request body for update operation
    {% set key_attr_names = [] -%}
    {% for attr in op["attrs"]|sort(attribute='name') -%}
    {% if attr.get("argument") -%}
    {% set _ = key_attr_names.append(attr["name"]) -%}
    {% endif -%}
    {% endfor -%}
    {% set seen_field_names = [] -%}
    let req_body = crate::models::Update{{ rsrc["comp_name_pascal"] }} {
        {% for attr in op["attrs"]|sort(attribute='name') -%}
        {% if attr["name"] not in seen_field_names -%}
        {% if attr.get("needs_none_in_update") -%}
        {{ attr["rust_name"] }}: None,
        {% set _ = seen_field_names.append(attr["name"]) -%}
        {% elif attr["name"] not in key_attr_names and not attr.get("argument") -%}
        {{ attr["rust_name"] }}: {% if attr.get("body_conversion") == "ENUM_MATCH" -%}args.{{ attr["rust_name"] }}.as_ref().map(|e| match e {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            {{ attr["type"] }}::{{ mapping["cli_variant"] }} => {{ attr.get("enum_model_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
        }){% elif attr.get("body_conversion") == "ENUM_ARRAY_MATCH" -%}
        args.{{ attr["rust_name"] }}.as_ref().map(|v| v.iter().map(|s| match s.to_lowercase().as_str() {
            {% for mapping in attr.get("enum_variant_mappings", []) -%}
            "{{ mapping["cli_variant"]|lower }}" => {{ attr.get("enum_item_type") }}::{{ mapping["model_variant"] }},
            {% endfor -%}
            _ => panic!("Invalid enum value for {{ attr["name"] }}: {}", s),
        }).collect()){% elif attr.get("body_conversion") -%}{{ attr["body_conversion"] }}{% else -%}args.{{ attr["rust_name"] }}.clone(){% endif -%},
        {% set _ = seen_field_names.append(attr["name"]) -%}
        {% endif -%}
        {% endif -%}
        {% endfor -%}
    };
    
    {% set key_attrs = [] -%}
    {% set seen_key_names = [] -%}
    {% for attr in op["attrs"]|sort(attribute='name') -%}
    {% if attr.get("argument") and attr["name"] not in seen_key_names -%}
    {% set _ = key_attrs.append(attr) -%}
    {% set _ = seen_key_names.append(attr["name"]) -%}
    {% endif -%}
    {% endfor -%}
    {% if key_attrs|length == 1 -%}
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, req_body, &args.{{ key_attrs[0]["rust_name"] }}).await?;
    {% else -%}
    // Multiple key arguments - adjust based on your API client signature
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, req_body, {% for attr in key_attrs -%}&args.{{ attr["rust_name"] }}{% if not loop.last %}, {% endif %}{% endfor -%}).await?;
    {% endif -%}
    {% else -%}
    // Build query parameters for instance operations
    {% set key_attrs = [] -%}
    {% for attr in op["attrs"] -%}
    {% if attr.get("argument") -%}
    {% set _ = key_attrs.append(attr) -%}
    {% endif -%}
    {% endfor -%}
    {% for qp in op.get("query_params", []) -%}
    {% if qp["type"] == "String" -%}
    {% if qp["required"] -%}
    let {{ qp["rust_name"] }}_param = Some(args.{{ qp["rust_name"] }}.as_str());
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }}.as_ref().map(|s| s.as_str());
    {% endif -%}
    {% elif qp["type"] == "i64" -%}
    {% if qp["required"] -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }} as i32;
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }}.map(|v| v as i32);
    {% endif -%}
    {% else -%}
    let {{ qp["rust_name"] }}_param = args.{{ qp["rust_name"] }};
    {% endif -%}
    {% endfor -%}
    {% if op["name"] == "delete" -%}
    // DELETE operations may return empty responses (204 No Content)
    // Handle gracefully without panicking on content type mismatches
    {% if key_attrs|length == 1 -%}
    let resp_result = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, &args.{{ key_attrs[0]["rust_name"] }}{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await;
    {% else -%}
    // Multiple key arguments - adjust based on your API client signature
    let resp_result = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, {% for attr in key_attrs -%}&args.{{ attr["rust_name"] }}{% if not loop.last %}, {% endif %}{% endfor -%}{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await;
    {% endif -%}
    match resp_result {
        Ok(data) => {
            // If we got JSON data, serialize and print it
            let json_output = serde_json::to_string_pretty(&data)?;
            println!("{}", json_output);
        },
        Err(crate::apis::Error::Serde(e)) => {
            // Check if this is a content type mismatch (empty response)
            let error_msg = e.to_string();
            if error_msg.contains("content type") || error_msg.contains("application/octet-stream") || error_msg.contains("cannot be converted") {
                // Empty response is OK for DELETE - operation succeeded
                // Output success JSON
                println!("{}", r#"{"status": "deleted"}"#);
            } else {
                // Real serialization error, propagate it
                return Err(Box::new(e));
            }
        },
        Err(e) => {
            // Other errors (network, HTTP errors, etc.) - propagate
            return Err(Box::new(e));
        }
    }
    {% else -%}
    {% if key_attrs|length == 1 -%}
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, &args.{{ key_attrs[0]["rust_name"] }}{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await?;
    {% else -%}
    // Multiple key arguments - adjust based on your API client signature
    let resp = crate::apis::{{ rsrc["name"] }}_api::{{ op["id"] }}(&ctx.api_client, {% for attr in key_attrs -%}&args.{{ attr["rust_name"] }}{% if not loop.last %}, {% endif %}{% endfor -%}{% for qp in op.get("query_params", []) %}, {{ qp["rust_name"] }}_param{% endfor %}).await?;
    {% endif -%}
    // Output response as JSON
    let json_output = serde_json::to_string_pretty(&resp)?;
    println!("{}", json_output);
    {% endif -%}
    {% endif -%}
    Ok(())
}

{% endfor -%}
