use serde::{Deserialize, Serialize};

// ---------------------------------------------------------------------------
// TaskBoard — container for a tracked task within a thread
// ---------------------------------------------------------------------------

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TaskBoardStatus {
    Active,
    Completed,
    Abandoned,
}

impl TaskBoardStatus {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Active => "active",
            Self::Completed => "completed",
            Self::Abandoned => "abandoned",
        }
    }

    pub fn from_db_str(s: &str) -> Self {
        match s {
            "active" => Self::Active,
            "completed" => Self::Completed,
            "abandoned" => Self::Abandoned,
            other => {
                tracing::warn!(
                    value = other,
                    "Unknown TaskBoardStatus from DB, defaulting to Active"
                );
                Self::Active
            }
        }
    }
}

#[derive(Debug, Clone)]
pub struct TaskBoardRecord {
    pub id: String,
    pub thread_id: String,
    pub title: String,
    pub status: TaskBoardStatus,
    pub active_task_id: Option<String>,
    pub created_at: String,
    pub updated_at: String,
}

/// DTO for task board exposed to frontend.
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TaskBoardDto {
    pub id: String,
    pub thread_id: String,
    pub title: String,
    pub status: TaskBoardStatus,
    pub active_task_id: Option<String>,
    pub tasks: Vec<super::task_item::TaskItemDto>,
    pub created_at: String,
    pub updated_at: String,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum QueryTaskScope {
    Active,
    All,
}

impl QueryTaskScope {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Active => "active",
            Self::All => "all",
        }
    }
}

fn default_query_task_scope() -> QueryTaskScope {
    QueryTaskScope::Active
}

// ---------------------------------------------------------------------------
// Input types for create_task tool
// ---------------------------------------------------------------------------

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTaskInput {
    pub title: String,
    pub steps: Vec<CreateTaskStep>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTaskStep {
    pub description: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct QueryTaskInput {
    #[serde(default = "default_query_task_scope")]
    pub scope: QueryTaskScope,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct QueryTaskResult {
    pub scope: QueryTaskScope,
    pub active_task_board_id: Option<String>,
    pub task_boards: Vec<TaskBoardDto>,
}

// ---------------------------------------------------------------------------
// Input types for update_task tool
// ---------------------------------------------------------------------------

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateTaskInput {
    pub task_board_id: String,
    #[serde(flatten)]
    pub action: UpdateTaskAction,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case", tag = "action")]
pub enum UpdateTaskAction {
    #[serde(rename_all = "camelCase")]
    StartStep {
        step_id: String,
    },
    #[serde(rename_all = "camelCase")]
    AdvanceStep {
        step_id: Option<String>,
    },
    #[serde(rename_all = "camelCase")]
    CompleteStep {
        step_id: String,
    },
    #[serde(rename_all = "camelCase")]
    FailStep {
        step_id: String,
        error_detail: String,
    },
    CompleteBoard,
    #[serde(rename_all = "camelCase")]
    AbandonBoard {
        reason: Option<String>,
    },
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn task_board_status_maps_database_strings_and_defaults_unknown_to_active() {
        assert_eq!(TaskBoardStatus::Active.as_str(), "active");
        assert_eq!(TaskBoardStatus::Completed.as_str(), "completed");
        assert_eq!(TaskBoardStatus::Abandoned.as_str(), "abandoned");

        assert_eq!(
            TaskBoardStatus::from_db_str("active"),
            TaskBoardStatus::Active
        );
        assert_eq!(
            TaskBoardStatus::from_db_str("completed"),
            TaskBoardStatus::Completed
        );
        assert_eq!(
            TaskBoardStatus::from_db_str("abandoned"),
            TaskBoardStatus::Abandoned
        );
        assert_eq!(
            TaskBoardStatus::from_db_str("paused"),
            TaskBoardStatus::Active
        );
    }

    #[test]
    fn query_task_input_defaults_scope_to_active() {
        let default_input: QueryTaskInput = serde_json::from_value(json!({})).unwrap();
        let explicit_all: QueryTaskInput =
            serde_json::from_value(json!({ "scope": "all" })).unwrap();

        assert_eq!(default_input.scope, QueryTaskScope::Active);
        assert_eq!(explicit_all.scope, QueryTaskScope::All);
        assert_eq!(QueryTaskScope::Active.as_str(), "active");
        assert_eq!(QueryTaskScope::All.as_str(), "all");
    }

    #[test]
    fn update_task_input_deserializes_tagged_actions() {
        let start: UpdateTaskInput = serde_json::from_value(json!({
            "taskBoardId": "board-1",
            "action": "start_step",
            "stepId": "step-1"
        }))
        .unwrap();
        assert_eq!(start.task_board_id, "board-1");
        match start.action {
            UpdateTaskAction::StartStep { step_id } => assert_eq!(step_id, "step-1"),
            _ => panic!("expected start_step action"),
        }

        let advance: UpdateTaskInput = serde_json::from_value(json!({
            "taskBoardId": "board-1",
            "action": "advance_step"
        }))
        .unwrap();
        match advance.action {
            UpdateTaskAction::AdvanceStep { step_id } => assert_eq!(step_id, None),
            _ => panic!("expected advance_step action"),
        }

        let fail: UpdateTaskInput = serde_json::from_value(json!({
            "taskBoardId": "board-1",
            "action": "fail_step",
            "stepId": "step-2",
            "errorDetail": "command failed"
        }))
        .unwrap();
        match fail.action {
            UpdateTaskAction::FailStep {
                step_id,
                error_detail,
            } => {
                assert_eq!(step_id, "step-2");
                assert_eq!(error_detail, "command failed");
            }
            _ => panic!("expected fail_step action"),
        }

        let abandon: UpdateTaskInput = serde_json::from_value(json!({
            "taskBoardId": "board-1",
            "action": "abandon_board",
            "reason": "obsolete"
        }))
        .unwrap();
        match abandon.action {
            UpdateTaskAction::AbandonBoard { reason } => {
                assert_eq!(reason.as_deref(), Some("obsolete"))
            }
            _ => panic!("expected abandon_board action"),
        }
    }
}
