use std::path::{Path, PathBuf};
use std::sync::Arc;

use anyhow::{Context, Result};
use sea_orm::DatabaseConnection;
use serde::Deserialize;

use robson_connector_bitbucket::BitbucketConfig;
use robson_connector_jira::JiraConfig;
use robson_gateway_slack::SlackAgentConfig;

pub use robson_connector_docker::ContainerConfig;

// --- WorkflowDbConfig ---

#[derive(Debug, Deserialize, Clone)]
pub struct WorkflowDbConfig {
    #[serde(default = "default_workflow_db_path")]
    pub db_path: String,
}

impl Default for WorkflowDbConfig {
    fn default() -> Self {
        Self {
            db_path: default_workflow_db_path(),
        }
    }
}

// --- OrchestratorConfig ---

#[derive(Debug, Deserialize, Clone)]
pub struct OrchestratorConfig {
    #[serde(default = "default_concurrency")]
    pub concurrency: usize,
    #[serde(default = "default_db_path")]
    pub db_path: String,
    #[serde(default = "default_webhook_port")]
    pub webhook_port: u16,
    pub claude_bin: Option<String>,
    pub api_token: Option<String>,
    pub ssh_key_path: Option<String>,
    #[serde(default = "default_git_name")]
    pub git_name: String,
    #[serde(default = "default_git_email")]
    pub git_email: String,
    #[serde(default)]
    pub parallel_workers: bool,
    /// Directory for per-task git clone workspaces. Relative paths are resolved against the
    /// process current working directory when the workflow runs.
    #[serde(default = "default_tmp_dir")]
    pub tmp_dir: String,
}

impl OrchestratorConfig {
    pub fn from_env() -> Self {
        Self {
            concurrency: std::env::var("ROBSON_AGENT_CONCURRENCY")
                .ok()
                .and_then(|v| v.parse().ok())
                .unwrap_or(1),
            db_path: std::env::var("ROBSON_AGENT_DB_PATH")
                .unwrap_or_else(|_| "robson-core.db".to_string()),
            webhook_port: std::env::var("ROBSON_AGENT_WEBHOOK_PORT")
                .ok()
                .and_then(|v| v.parse().ok())
                .unwrap_or(3000),
            claude_bin: std::env::var("ROBSON_AGENT_CLAUDE_BIN").ok(),
            api_token: std::env::var("ROBSON_AGENT_API_TOKEN").ok(),
            ssh_key_path: std::env::var("ROBSON_AGENT_SSH_KEY_PATH").ok(),
            git_name: std::env::var("ROBSON_AGENT_GIT_NAME")
                .unwrap_or_else(|_| "Robson Agent".to_string()),
            git_email: std::env::var("ROBSON_AGENT_GIT_EMAIL")
                .unwrap_or_else(|_| "queiroz@investtools.com.br".to_string()),
            parallel_workers: std::env::var("ROBSON_AGENT_PARALLEL_WORKERS")
                .ok()
                .and_then(|v| v.parse().ok())
                .unwrap_or(false),
            tmp_dir: std::env::var("ROBSON_AGENT_TMP_DIR").unwrap_or_else(|_| ".tmp".to_string()),
        }
    }

    /// Resolved root under which `tempfile` creates each task's isolated clone directory.
    pub fn resolve_implementation_tmp_root(&self) -> Result<PathBuf> {
        let path = Path::new(&self.tmp_dir);
        if path.is_absolute() {
            Ok(path.to_path_buf())
        } else {
            let cwd = std::env::current_dir()
                .context("Failed to resolve project root (current working directory)")?;
            Ok(cwd.join(path))
        }
    }
}

// --- WorkflowConfig ---

#[derive(Debug, Deserialize, Clone)]
pub struct WorkflowConfig {
    #[serde(rename = "agent")]
    pub orchestrator: OrchestratorConfig,
    #[serde(default)]
    pub workflow: WorkflowDbConfig,
    pub jira: JiraConfig,
    pub bitbucket: Option<BitbucketConfig>,
    #[serde(default)]
    pub slack_agent: SlackAgentConfig,
    #[serde(default)]
    pub container: ContainerConfig,
}

impl WorkflowDbConfig {
    pub fn from_env() -> Self {
        Self {
            db_path: std::env::var("ROBSON_WORKFLOW_DB_PATH")
                .unwrap_or_else(|_| "robson-workflow.db".to_string()),
        }
    }
}

impl WorkflowConfig {
    pub fn from_env() -> Self {
        Self {
            orchestrator: OrchestratorConfig::from_env(),
            workflow: WorkflowDbConfig::from_env(),
            jira: JiraConfig::from_env(),
            bitbucket: Some(BitbucketConfig::from_env()),
            slack_agent: SlackAgentConfig::from_env(),
            container: ContainerConfig::from_env(),
        }
    }
}

// --- WorkflowState ---

#[derive(Clone)]
pub struct WorkflowState {
    pub workflow_db: DatabaseConnection,
    pub config: WorkflowConfig,
    /// Agent DB (robson-core DB) used to emit `ProcessEvent` rows during the pipeline.
    /// `None` when the workflow is triggered from the CLI (no agent context).
    pub agent_db: Option<DatabaseConnection>,
    /// The `conversations.id` that triggered this workflow run.
    /// `None` when triggered from the CLI.
    pub conversation_id: Option<i32>,
}

impl WorkflowState {
    pub fn new(workflow_db: DatabaseConnection, config: WorkflowConfig) -> Arc<Self> {
        Arc::new(Self {
            workflow_db,
            config,
            agent_db: None,
            conversation_id: None,
        })
    }

    pub fn new_with_agent_context(
        workflow_db: DatabaseConnection,
        config: WorkflowConfig,
        agent_db: DatabaseConnection,
        conversation_id: i32,
    ) -> Arc<Self> {
        Arc::new(Self {
            workflow_db,
            config,
            agent_db: Some(agent_db),
            conversation_id: Some(conversation_id),
        })
    }
}

// --- Default helpers ---

fn default_concurrency() -> usize {
    1
}
fn default_db_path() -> String {
    "robson-core.db".to_string()
}
fn default_workflow_db_path() -> String {
    "robson-workflow.db".to_string()
}
fn default_webhook_port() -> u16 {
    3000
}
fn default_git_name() -> String {
    "Robson Agent".to_string()
}
fn default_git_email() -> String {
    "queiroz@investtools.com.br".to_string()
}
fn default_tmp_dir() -> String {
    ".tmp".to_string()
}
