use std::fs;

use anyhow::{Context, Result};
use tempfile::{Builder, TempDir};
use tracing::{debug, error, info};

use crate::config::WorkflowState;
use crate::entities::repo::Model as Repo;
use crate::entities::task::Model as Task;
use crate::entities::task_execution::Model as TaskExecution;

use robson_core::plugin::ProcessEventKind;

use super::context::TaskExecutionContext;
use super::emit_process_event;
use super::implementation::{build_clone_url_for_repo, clone_repository};

pub async fn phase1_setup(
    state: &WorkflowState,
    task: &Task,
) -> Result<(TaskExecutionContext, Repo, TempDir)> {
    let config = &state.config;
    let db = &state.workflow_db;
    let key = &task.jira_key;

    debug!(jira_key = %key, "phase1 setup starting");

    let ex = TaskExecution::find_in_progress_for_task(db, task.id)
        .await?
        .ok_or_else(|| {
            anyhow::anyhow!(
                "Task {} has no in_progress task_executions row. Re-create the task with `task create`.",
                key
            )
        })?;

    let description_text = ex.description.clone().unwrap_or_else(|| ex.summary.clone());

    let jira_url = format!(
        "{}/browse/{}",
        config.jira.base_url.trim_end_matches('/'),
        key
    );

    let repo = Repo::find_by_id(db, ex.repo_id).await?.ok_or_else(|| {
        anyhow::anyhow!(
            "Repo id {} for task {} is missing from the database.",
            ex.repo_id,
            key
        )
    })?;

    let ctx = TaskExecutionContext::new(
        ex.summary.clone(),
        description_text,
        jira_url,
        ex.assignee.clone(),
        repo.slug.clone(),
    );

    emit_process_event(
        state,
        ProcessEventKind::Started,
        &format!(":rocket: Task *{}* started: {}", key, ctx.summary),
    )
    .await;
    emit_process_event(
        state,
        ProcessEventKind::Progress,
        &format!(":wrench: Setting up repo `{}` for *{}*…", repo.slug, key),
    )
    .await;

    let clone_url = build_clone_url_for_repo(config, &repo)?;
    let tmp_root = config.orchestrator.resolve_implementation_tmp_root()?;
    fs::create_dir_all(&tmp_root)
        .with_context(|| format!("Failed to create {}", tmp_root.display()))?;
    let temp_dir = Builder::new()
        .prefix(&format!("{}-", key))
        .tempdir_in(&tmp_root)
        .with_context(|| {
            format!(
                "Failed to create temp directory under {}",
                tmp_root.display()
            )
        })?;

    let ssh_key = config.orchestrator.ssh_key_path.as_deref();
    match clone_repository(&clone_url, &repo, temp_dir.path(), ssh_key) {
        Ok(()) => info!("[{}][{}] Clone successful.", key, repo.slug),
        Err(e) => {
            error!("[{}][{}] Clone failed: {:#}", key, repo.slug, e);
            return Err(e);
        }
    }

    info!(
        jira_key = %key,
        repo_slug = %repo.slug,
        repo_url = %repo.slug,
        "phase1 setup complete"
    );
    Ok((ctx, repo, temp_dir))
}
