use anyhow::Result;
use clap::Subcommand;
use serde::{Deserialize, Serialize};

use crate::client::ApiClient;
use crate::config::CliConfig;
use crate::output::{self, OutputFormat};

#[derive(Subcommand)]
pub enum TriggerCommands {
    /// List all triggers
    List {
        /// Filter by pack name
        #[arg(long)]
        pack: Option<String>,
    },
    /// Show details of a specific trigger
    Show {
        /// Trigger reference (pack.trigger or ID)
        trigger_ref: String,
    },
    /// Update a trigger
    Update {
        /// Trigger reference (pack.trigger or ID)
        trigger_ref: String,

        /// Update label
        #[arg(long)]
        label: Option<String>,

        /// Update description
        #[arg(long)]
        description: Option<String>,

        /// Update enabled status
        #[arg(long)]
        enabled: Option<bool>,
    },
    /// Delete a trigger
    Delete {
        /// Trigger reference (pack.trigger or ID)
        trigger_ref: String,

        /// Skip confirmation prompt
        #[arg(long)]
        yes: bool,
    },
}

#[derive(Debug, Serialize, Deserialize)]
struct Trigger {
    id: i64,
    #[serde(rename = "ref")]
    trigger_ref: String,
    #[serde(default)]
    pack: Option<i64>,
    #[serde(default)]
    pack_ref: Option<String>,
    label: String,
    description: Option<String>,
    enabled: bool,
    #[serde(default)]
    param_schema: Option<serde_json::Value>,
    #[serde(default)]
    out_schema: Option<serde_json::Value>,
    #[serde(default)]
    webhook_enabled: Option<bool>,
    #[serde(default)]
    webhook_key: Option<String>,
    created: String,
    updated: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TriggerDetail {
    id: i64,
    #[serde(rename = "ref")]
    trigger_ref: String,
    #[serde(default)]
    pack: Option<i64>,
    #[serde(default)]
    pack_ref: Option<String>,
    label: String,
    description: Option<String>,
    enabled: bool,
    #[serde(default)]
    param_schema: Option<serde_json::Value>,
    #[serde(default)]
    out_schema: Option<serde_json::Value>,
    #[serde(default)]
    webhook_enabled: Option<bool>,
    #[serde(default)]
    webhook_key: Option<String>,
    created: String,
    updated: String,
}

pub async fn handle_trigger_command(
    profile: &Option<String>,
    command: TriggerCommands,
    api_url: &Option<String>,
    output_format: OutputFormat,
) -> Result<()> {
    match command {
        TriggerCommands::List { pack } => handle_list(pack, profile, api_url, output_format).await,
        TriggerCommands::Show { trigger_ref } => {
            handle_show(trigger_ref, profile, api_url, output_format).await
        }
        TriggerCommands::Update {
            trigger_ref,
            label,
            description,
            enabled,
        } => {
            handle_update(
                trigger_ref,
                label,
                description,
                enabled,
                profile,
                api_url,
                output_format,
            )
            .await
        }
        TriggerCommands::Delete { trigger_ref, yes } => {
            handle_delete(trigger_ref, yes, profile, api_url, output_format).await
        }
    }
}

async fn handle_list(
    pack: Option<String>,
    profile: &Option<String>,
    api_url: &Option<String>,
    output_format: OutputFormat,
) -> Result<()> {
    let config = CliConfig::load_with_profile(profile.as_deref())?;
    let mut client = ApiClient::from_config(&config, api_url);

    let path = if let Some(pack_name) = pack {
        format!("/triggers?pack={}", pack_name)
    } else {
        "/triggers".to_string()
    };

    let triggers: Vec<Trigger> = client.get(&path).await?;

    match output_format {
        OutputFormat::Json | OutputFormat::Yaml => {
            output::print_output(&triggers, output_format)?;
        }
        OutputFormat::Table => {
            if triggers.is_empty() {
                output::print_info("No triggers found");
            } else {
                let mut table = output::create_table();
                output::add_header(&mut table, vec!["ID", "Pack", "Name", "Description"]);

                for trigger in triggers {
                    table.add_row(vec![
                        trigger.id.to_string(),
                        trigger.pack_ref.as_deref().unwrap_or("").to_string(),
                        trigger.label.clone(),
                        output::truncate(&trigger.description.unwrap_or_default(), 50),
                    ]);
                }

                println!("{}", table);
            }
        }
    }

    Ok(())
}

async fn handle_show(
    trigger_ref: String,
    profile: &Option<String>,
    api_url: &Option<String>,
    output_format: OutputFormat,
) -> Result<()> {
    let config = CliConfig::load_with_profile(profile.as_deref())?;
    let mut client = ApiClient::from_config(&config, api_url);

    let path = format!("/triggers/{}", trigger_ref);
    let trigger: TriggerDetail = client.get(&path).await?;

    match output_format {
        OutputFormat::Json | OutputFormat::Yaml => {
            output::print_output(&trigger, output_format)?;
        }
        OutputFormat::Table => {
            output::print_section(&format!("Trigger: {}", trigger.trigger_ref));
            output::print_key_value_table(vec![
                ("ID", trigger.id.to_string()),
                ("Ref", trigger.trigger_ref.clone()),
                (
                    "Pack",
                    trigger.pack_ref.as_deref().unwrap_or("None").to_string(),
                ),
                ("Label", trigger.label.clone()),
                (
                    "Description",
                    trigger.description.unwrap_or_else(|| "None".to_string()),
                ),
                ("Enabled", output::format_bool(trigger.enabled)),
                (
                    "Webhook Enabled",
                    output::format_bool(trigger.webhook_enabled.unwrap_or(false)),
                ),
                ("Created", output::format_timestamp(&trigger.created)),
                ("Updated", output::format_timestamp(&trigger.updated)),
            ]);

            if let Some(webhook_key) = &trigger.webhook_key {
                output::print_section("Webhook");
                output::print_info(&format!("Key: {}", webhook_key));
            }

            if let Some(param_schema) = &trigger.param_schema {
                if !param_schema.is_null() {
                    output::print_section("Parameter Schema");
                    println!("{}", serde_json::to_string_pretty(param_schema)?);
                }
            }

            if let Some(out_schema) = &trigger.out_schema {
                if !out_schema.is_null() {
                    output::print_section("Output Schema");
                    println!("{}", serde_json::to_string_pretty(out_schema)?);
                }
            }
        }
    }

    Ok(())
}

async fn handle_update(
    trigger_ref: String,
    label: Option<String>,
    description: Option<String>,
    enabled: Option<bool>,
    profile: &Option<String>,
    api_url: &Option<String>,
    output_format: OutputFormat,
) -> Result<()> {
    let config = CliConfig::load_with_profile(profile.as_deref())?;
    let mut client = ApiClient::from_config(&config, api_url);

    // Check that at least one field is provided
    if label.is_none() && description.is_none() && enabled.is_none() {
        anyhow::bail!("At least one field must be provided to update");
    }

    #[derive(Serialize)]
    #[serde(tag = "op", content = "value", rename_all = "snake_case")]
    enum TriggerDescriptionPatch {
        Set(String),
    }

    #[derive(Serialize)]
    struct UpdateTriggerRequest {
        #[serde(skip_serializing_if = "Option::is_none")]
        label: Option<String>,
        #[serde(skip_serializing_if = "Option::is_none")]
        description: Option<TriggerDescriptionPatch>,
        #[serde(skip_serializing_if = "Option::is_none")]
        enabled: Option<bool>,
    }

    let request = UpdateTriggerRequest {
        label,
        description: description.map(TriggerDescriptionPatch::Set),
        enabled,
    };

    let path = format!("/triggers/{}", trigger_ref);
    let trigger: TriggerDetail = client.put(&path, &request).await?;

    match output_format {
        OutputFormat::Json | OutputFormat::Yaml => {
            output::print_output(&trigger, output_format)?;
        }
        OutputFormat::Table => {
            output::print_success(&format!(
                "Trigger '{}' updated successfully",
                trigger.trigger_ref
            ));
            output::print_key_value_table(vec![
                ("ID", trigger.id.to_string()),
                ("Ref", trigger.trigger_ref.clone()),
                (
                    "Pack",
                    trigger.pack_ref.as_deref().unwrap_or("None").to_string(),
                ),
                ("Label", trigger.label.clone()),
                (
                    "Description",
                    trigger.description.unwrap_or_else(|| "None".to_string()),
                ),
                ("Enabled", output::format_bool(trigger.enabled)),
                ("Updated", output::format_timestamp(&trigger.updated)),
            ]);
        }
    }

    Ok(())
}

async fn handle_delete(
    trigger_ref: String,
    yes: bool,
    profile: &Option<String>,
    api_url: &Option<String>,
    output_format: OutputFormat,
) -> Result<()> {
    let config = CliConfig::load_with_profile(profile.as_deref())?;
    let mut client = ApiClient::from_config(&config, api_url);

    // Confirm deletion unless --yes is provided
    if !yes && matches!(output_format, OutputFormat::Table) {
        let confirm = dialoguer::Confirm::new()
            .with_prompt(format!(
                "Are you sure you want to delete trigger '{}'?",
                trigger_ref
            ))
            .default(false)
            .interact()?;

        if !confirm {
            output::print_info("Delete cancelled");
            return Ok(());
        }
    }

    let path = format!("/triggers/{}", trigger_ref);
    client.delete_no_response(&path).await?;

    match output_format {
        OutputFormat::Json | OutputFormat::Yaml => {
            let msg = serde_json::json!({"message": "Trigger deleted successfully"});
            output::print_output(&msg, output_format)?;
        }
        OutputFormat::Table => {
            output::print_success(&format!("Trigger '{}' deleted successfully", trigger_ref));
        }
    }

    Ok(())
}
