use anyhow::Result;
use sea_orm::DatabaseConnection;
use tracing::{debug, error, info};

use crate::config::{WorkflowConfig, WorkflowState};
use crate::entities::task::Model as Task;
use crate::entities::task_execution::Model as TaskExecution;
use robson_connector_jira::JiraClient;
use robson_core::plugin::ProcessEventKind;
use robson_gateway_slack::SlackClient;

use super::context::TaskExecutionContext;
use super::emit_process_event;

fn format_slack_preview(
    config: &WorkflowConfig,
    task: &Task,
    ctx: &TaskExecutionContext,
    branch_name: &str,
    pr_url: &str,
    jira_url: &str,
) -> String {
    let channel = &config.slack_agent.default_channel;
    let assignee_text = ctx
        .assignee
        .as_deref()
        .map(|a| format!("*Assignee:* {}", a))
        .unwrap_or_else(|| "*Assignee:* Unassigned".to_string());
    format!(
        "Channel: {}\n:rocket: *PR Ready for Review*\n*Task:* <{}|{}> — {}\n*Branch:* `{}`\n*PR:* <{}|View Pull Request>\n{}",
        channel, jira_url, task.jira_key, ctx.summary, branch_name, pr_url, assignee_text
    )
}

pub async fn comment_on_jira(
    config: &WorkflowConfig,
    jira_key: &str,
    branch_name: &str,
    pr_url: &str,
) -> Result<()> {
    debug!(jira_key = %jira_key, branch_name = %branch_name, "posting jira comment");
    JiraClient::new(config.jira.clone())?
        .add_comment(jira_key, branch_name, pr_url)
        .await?;
    info!(jira_key = %jira_key, branch_name = %branch_name, "jira comment posted");
    Ok(())
}

pub async fn notify_slack(
    config: &WorkflowConfig,
    task: &Task,
    ctx: &TaskExecutionContext,
    branch_name: &str,
    pr_url: &str,
    jira_url: &str,
) -> Result<()> {
    let channel = &config.slack_agent.default_channel;
    debug!(
        jira_key = %task.jira_key,
        channel = %channel,
        branch_name = %branch_name,
        "sending slack review notification"
    );
    SlackClient::new(config.slack_agent.token.clone())?
        .notify_review(
            channel,
            &task.jira_key,
            &ctx.summary,
            branch_name,
            pr_url,
            jira_url,
            ctx.assignee.as_deref(),
        )
        .await?;
    info!(jira_key = %task.jira_key, channel = %channel, "slack review notification sent");
    Ok(())
}

pub async fn notify_validation_failure(
    config: &WorkflowConfig,
    task: &Task,
    ctx: &TaskExecutionContext,
    jira_url: &str,
    state: &WorkflowState,
) -> Result<()> {
    let message = format!(
        "Automated validation failed for task {} — Docker test service exited with non-zero status. The implementation branch was committed but no PR was created.",
        task.jira_key
    );

    debug!(jira_key = %task.jira_key, "posting jira validation failure comment");
    if let Err(e) = JiraClient::new(config.jira.clone())?
        .add_text_comment(&task.jira_key, &message)
        .await
    {
        error!(jira_key = %task.jira_key, error = %e, "failed to post jira validation failure comment");
    } else {
        info!(jira_key = %task.jira_key, "jira validation failure comment posted");
    }

    let slack_message = format!(
        ":x: *Validation Failed*\n*Task:* <{}|{}> — {}\n*Reason:* Docker test service failed. No PR created.",
        jira_url, task.jira_key, ctx.summary
    );

    if state.conversation_id.is_some() {
        emit_process_event(state, ProcessEventKind::Failed, &slack_message).await;
        info!(jira_key = %task.jira_key, "validation failure event emitted to originating channel");
    } else {
        let channel = &config.slack_agent.default_channel;
        debug!(jira_key = %task.jira_key, channel = %channel, "sending slack validation failure notification");
        if let Err(e) = SlackClient::new(config.slack_agent.token.clone())?
            .post_message(channel, &slack_message)
            .await
        {
            error!(jira_key = %task.jira_key, channel = %channel, error = %e, "failed to send slack validation failure notification");
        } else {
            info!(jira_key = %task.jira_key, channel = %channel, "slack validation failure notification sent");
        }
    }

    Ok(())
}

pub async fn run_notification(
    config: &WorkflowConfig,
    db: &DatabaseConnection,
    task: &Task,
    ctx: &TaskExecutionContext,
    no_notify: bool,
    state: &WorkflowState,
) -> Result<()> {
    debug!(jira_key = %task.jira_key, no_notify = no_notify, "run_notification starting");

    let db_task = Task::find_by_key(db, &task.jira_key)
        .await?
        .ok_or_else(|| {
            anyhow::anyhow!(
                "Task {} not found in DB during notification phase",
                task.jira_key
            )
        })?;

    let ex = TaskExecution::find_latest_for_task(db, db_task.id)
        .await?
        .ok_or_else(|| {
            anyhow::anyhow!(
                "Task {} has no task_executions row during notification phase",
                task.jira_key
            )
        })?;

    let branch_name = ex.branch_name.clone().unwrap_or_default();
    let pr_url = ex.pr_url.clone().unwrap_or_default();

    if no_notify {
        let pr_display = if pr_url.is_empty() {
            "(no PR — run without --no-push to create one)".to_string()
        } else {
            pr_url.clone()
        };
        let slack_preview =
            format_slack_preview(config, task, ctx, &branch_name, &pr_url, &ctx.jira_url);
        info!(
            jira_key = %task.jira_key,
            branch_name = %branch_name,
            pr_url = %pr_display,
            "--no-notify: skipping jira comment and slack notification"
        );
        println!(
            "[{}] --no-notify: skipping Jira comment and Slack notification.\n  Jira comment: Branch '{}' | PR: {}\n  Slack preview:\n{}",
            task.jira_key, branch_name, pr_display, slack_preview,
        );
        return Ok(());
    }

    // Always post the Jira comment regardless of gateway.
    match comment_on_jira(config, &task.jira_key, &branch_name, &pr_url).await {
        Ok(()) => {
            info!(jira_key = %task.jira_key, branch_name = %branch_name, "jira comment posted")
        }
        Err(e) => {
            error!(jira_key = %task.jira_key, error = %e, "jira comment failed");
            return Err(e);
        }
    }

    // Route the PR-ready Slack message:
    //   - If triggered via an agent gateway: emit a ProcessEvent so the SensoriumLoop
    //     delivers it back to the originating channel.
    //   - Otherwise fall back to posting directly to the configured default_channel.
    if state.conversation_id.is_some() {
        let content = format!(
            ":rocket: *PR Ready for Review*\n*Task:* <{}|{}> — {}\n*Branch:* `{}`\n*PR:* <{}|View Pull Request>\n{}",
            ctx.jira_url,
            task.jira_key,
            ctx.summary,
            branch_name,
            pr_url,
            ctx.assignee.as_deref()
                .map(|a| format!("*Assignee:* {}", a))
                .unwrap_or_else(|| "*Assignee:* Unassigned".to_string()),
        );
        emit_process_event(state, ProcessEventKind::Completed, &content).await;
        info!(jira_key = %task.jira_key, "pr-ready event emitted to originating channel");
    } else {
        let channel = &config.slack_agent.default_channel;
        match notify_slack(config, task, ctx, &branch_name, &pr_url, &ctx.jira_url).await {
            Ok(()) => {
                info!(jira_key = %task.jira_key, channel = %channel, "slack notification sent")
            }
            Err(e) => {
                error!(jira_key = %task.jira_key, channel = %channel, error = %e, "slack notification failed");
                return Err(e);
            }
        }
    }

    Ok(())
}
