//! Chat endpoint for LLM-powered contract management.
//!
//! This handler provides an agentic loop for managing contracts: creating tasks,
//! adding files, managing repositories, and handling phase transitions.

use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::IntoResponse,
    Json,
};
use serde::{Deserialize, Serialize};
use serde_json::json;
use utoipa::ToSchema;
use uuid::Uuid;

use crate::db::{
    models::{ContractChatHistoryResponse, ContractWithRelations, CreateTaskRequest, UpdateFileRequest},
    repository,
};
use crate::llm::{
    all_templates, analyze_task_output, body_to_markdown, format_checklist_markdown,
    format_parsed_tasks, get_phase_checklist, parse_tasks_from_breakdown,
    claude::{self, ClaudeClient, ClaudeError, ClaudeModel},
    groq::{GroqClient, GroqError, Message, ToolCallResponse},
    parse_contract_tool_call, templates_for_phase, ContractToolRequest, FileInfo,
    LlmModel, TaskInfo, ToolCall, ToolResult, UserQuestion, CONTRACT_TOOLS,
    format_transcript_for_analysis, calculate_speaker_stats,
    build_analysis_prompt, parse_analysis_response,
};
use crate::server::auth::Authenticated;
use crate::server::state::{DaemonCommand, SharedState};

/// Maximum number of tool-calling rounds to prevent infinite loops
const MAX_TOOL_ROUNDS: usize = 30;

#[derive(Debug, Clone, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ContractChatHistoryMessage {
    /// Role: "user" or "assistant"
    pub role: String,
    /// Message content
    pub content: String,
}

#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ContractChatRequest {
    /// The user's message/instruction
    pub message: String,
    /// Optional model selection: "claude-sonnet" (default), "claude-opus", or "groq"
    #[serde(default)]
    pub model: Option<String>,
    /// Optional conversation history for context continuity
    #[serde(default)]
    pub history: Option<Vec<ContractChatHistoryMessage>>,
}

#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ContractChatResponse {
    /// The LLM's response message
    pub response: String,
    /// Tool calls that were executed
    pub tool_calls: Vec<ContractToolCallInfo>,
    /// Questions pending user answers (pauses conversation)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub pending_questions: Option<Vec<UserQuestion>>,
}

#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct ContractToolCallInfo {
    pub name: String,
    pub result: ToolResult,
}

/// Enum to hold LLM clients
enum LlmClient {
    Groq(GroqClient),
    Claude(ClaudeClient),
}

/// Unified result from LLM call
struct LlmResult {
    content: Option<String>,
    tool_calls: Vec<ToolCall>,
    raw_tool_calls: Vec<ToolCallResponse>,
    finish_reason: String,
}

/// Helper to get contract with all relations
async fn get_contract_with_relations(
    pool: &sqlx::PgPool,
    contract_id: Uuid,
    owner_id: Uuid,
) -> Result<Option<ContractWithRelations>, sqlx::Error> {
    let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await? {
        Some(c) => c,
        None => return Ok(None),
    };

    let repositories = repository::list_contract_repositories(pool, contract_id)
        .await
        .unwrap_or_default();

    let files = repository::list_files_in_contract(pool, contract_id, owner_id)
        .await
        .unwrap_or_default();

    let tasks = repository::list_tasks_in_contract(pool, contract_id, owner_id)
        .await
        .unwrap_or_default();

    Ok(Some(ContractWithRelations {
        contract,
        repositories,
        files,
        tasks,
    }))
}

/// Chat with a contract using LLM tool calling for management
#[utoipa::path(
    post,
    path = "/api/v1/contracts/{id}/chat",
    request_body = ContractChatRequest,
    responses(
        (status = 200, description = "Chat completed successfully", body = ContractChatResponse),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Contract not found"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = Uuid, Path, description = "Contract ID")
    ),
    security(
        ("bearer_auth" = []),
        ("api_key" = [])
    ),
    tag = "Contracts"
)]
pub async fn contract_chat_handler(
    State(state): State<SharedState>,
    Authenticated(auth): Authenticated,
    Path(contract_id): Path<Uuid>,
    Json(request): Json<ContractChatRequest>,
) -> impl IntoResponse {
    // Check if database is configured
    let Some(ref pool) = state.db_pool else {
        return (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(json!({ "error": "Database not configured" })),
        )
            .into_response();
    };

    // Get the contract (scoped by owner)
    let contract = match get_contract_with_relations(pool, contract_id, auth.owner_id).await {
        Ok(Some(c)) => c,
        Ok(None) => {
            return (
                StatusCode::NOT_FOUND,
                Json(json!({ "error": "Contract not found" })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Database error: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Database error: {}", e) })),
            )
                .into_response();
        }
    };

    // Parse model selection (default to Claude Sonnet)
    let model = request
        .model
        .as_ref()
        .and_then(|m| LlmModel::from_str(m))
        .unwrap_or(LlmModel::ClaudeSonnet);

    tracing::info!("Contract chat using LLM model: {:?}", model);

    // Initialize the appropriate LLM client
    let llm_client = match model {
        LlmModel::ClaudeSonnet => match ClaudeClient::from_env(ClaudeModel::Sonnet) {
            Ok(client) => LlmClient::Claude(client),
            Err(ClaudeError::MissingApiKey) => {
                return (
                    StatusCode::SERVICE_UNAVAILABLE,
                    Json(json!({ "error": "ANTHROPIC_API_KEY not configured" })),
                )
                    .into_response();
            }
            Err(e) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(json!({ "error": format!("Claude client error: {}", e) })),
                )
                    .into_response();
            }
        },
        LlmModel::ClaudeOpus => match ClaudeClient::from_env(ClaudeModel::Opus) {
            Ok(client) => LlmClient::Claude(client),
            Err(ClaudeError::MissingApiKey) => {
                return (
                    StatusCode::SERVICE_UNAVAILABLE,
                    Json(json!({ "error": "ANTHROPIC_API_KEY not configured" })),
                )
                    .into_response();
            }
            Err(e) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(json!({ "error": format!("Claude client error: {}", e) })),
                )
                    .into_response();
            }
        },
        LlmModel::GroqKimi => match GroqClient::from_env() {
            Ok(client) => LlmClient::Groq(client),
            Err(GroqError::MissingApiKey) => {
                return (
                    StatusCode::SERVICE_UNAVAILABLE,
                    Json(json!({ "error": "GROQ_API_KEY not configured" })),
                )
                    .into_response();
            }
            Err(e) => {
                return (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(json!({ "error": format!("Groq client error: {}", e) })),
                )
                    .into_response();
            }
        },
    };

    // Build contract context
    let contract_context = build_contract_context(&contract);

    // Build system prompt for contract management
    let system_prompt = format!(
        r#"You are an intelligent contract management agent. You guide users through the contract lifecycle from research to completion, helping them organize work, create documentation, set up repositories, and execute tasks.

## Your Capabilities
You have access to tools for:
- **Query**: get_contract_status, list_contract_files, list_contract_tasks, list_contract_repositories, read_file
- **File Management**: create_file_from_template, create_empty_file, list_available_templates
- **Task Management**: create_contract_task, delegate_content_generation, start_task
- **Phase Management**: get_phase_info, suggest_phase_transition, advance_phase
- **Repository Management**: list_daemon_directories, add_repository, set_primary_repository
- **Interactive**: ask_user

## Content Generation Deferral
When asked to write substantial content, fill templates, or generate documentation:
- **Use delegate_content_generation** to create a task for the content generation
- This delegates the work to a task agent that can do more thorough research and writing

**Use delegation for:**
- Filling in template content with real data
- Writing documentation based on requirements
- Generating user stories or specifications
- Creating detailed design documents
- Any substantial writing that requires research or analysis

**Direct actions (no delegation needed):**
- Listing files/tasks/repos
- Reading files
- Phase transitions
- Creating empty files or templates
- Simple queries and status checks
- Asking user questions

## Contract Lifecycle Phases

### 1. RESEARCH Phase
**Purpose**: Gather information and understand the problem space
**Key Activities**:
- Conduct user research and interviews
- Analyze competitors and existing solutions
- Document findings and insights
- Identify opportunities and constraints
**Suggested Actions**:
- Create a "Research Notes" document to capture findings
- Create a "Competitor Analysis" document
- When research is complete, suggest transitioning to Specify phase

### 2. SPECIFY Phase
**Purpose**: Define what needs to be built
**Key Activities**:
- Write clear requirements
- Create user stories with acceptance criteria
- Define scope and constraints
- Document technical constraints
**Suggested Actions**:
- Create a "Requirements" document
- Create "User Stories" with acceptance criteria
- When specifications are clear, suggest transitioning to Plan phase

### 3. PLAN Phase
**Purpose**: Design the solution and break down the work
**Key Activities**:
- Design system architecture
- Create technical specifications
- Break work into implementable tasks
- Set up repositories for development
**Suggested Actions**:
- Create an "Architecture" document
- Create a "Task Breakdown" document
- **IMPORTANT**: Help set up a repository if not already configured
- When planning is complete and a repository is set, suggest transitioning to Execute phase

### 4. EXECUTE Phase
**Purpose**: Implement the solution
**Key Activities**:
- Create and run tasks to implement features
- Write and run tests
- Track progress
- Document implementation decisions
**Suggested Actions**:
- Create tasks based on the task breakdown
- Monitor task progress and help resolve blockers
- When all tasks are complete, suggest transitioning to Review phase

### 5. REVIEW Phase
**Purpose**: Validate and document the completed work
**Key Activities**:
- Review completed work
- Create release notes
- Conduct retrospective
- Document learnings
**Suggested Actions**:
- Create a "Release Notes" document
- Create a "Retrospective" document
- Help mark the contract as complete when review is done

## Current Contract
{contract_context}

## Proactive Guidance

### Repository Setup (Critical for Plan/Execute phases)
When the user wants to add a local repository or set up for execution:
1. **First call list_daemon_directories** to get available paths from connected agents
2. Present the suggested directories to the user
3. Ask which path they want to use, or let them specify a custom path
4. Then call add_repository with the chosen path

Example flow:
```
User: "Set up a repository for this contract"
You: Call list_daemon_directories first
You: "I found these directories from your connected agent:
      - /Users/alice/projects (Working Directory)
      - /Users/alice/.makima/home (Makima Home)
      Which would you like to use, or provide a custom path?"
```

### Phase Transitions
- Phases progress in order: research -> specify -> plan -> execute -> review
- You can ONLY advance forward one step at a time to the NEXT phase
- ALWAYS use suggest_phase_transition FIRST to get the exact nextPhase value
- Then use advance_phase with that exact nextPhase value
- Example: If currentPhase is "specify", nextPhase will be "plan" - use advance_phase with new_phase="plan"
- NEVER suggest advancing to the same phase the contract is already in

### New Users
When a new contract is created or the user seems unsure:
1. Explain the current phase and what should be done
2. Suggest creating appropriate documents
3. Guide them toward the next milestone

## Agentic Behavior Guidelines

### 1. Understand Before Acting
- For complex requests, first gather information about the contract's current state
- Use get_contract_status or list_contract_files to understand what exists
- Consider the current phase when suggesting actions

### 2. Phase-Appropriate Suggestions
- Suggest templates and actions appropriate for the current phase
- When creating files, prefer templates that match the contract's phase
- Advise when the contract might be ready for the next phase

### 3. Help Plan Work
- When asked to plan work, read existing files to understand context
- Suggest creating tasks based on requirements or plans in files
- Offer to create task breakdowns from design documents

### 4. Repository Management
- When adding local repositories, ALWAYS use list_daemon_directories first to get suggestions
- This provides the user with valid paths from their connected agents
- Don't ask users to manually type paths when suggestions are available

### 5. Task Creation and Execution
- When creating tasks, derive plans from existing contract files when possible
- Use the contract's primary repository for tasks by default
- Create clear, actionable task plans
- After creating a task, you can use **start_task** to immediately begin execution
- A daemon must be connected for start_task to work

### 6. Be Proactive but Efficient
- Guide users through the contract flow
- Don't over-analyze simple requests
- Use the minimum number of tool calls needed
- Provide clear summaries of actions taken

## Important Notes
- This contract's ID is: {contract_id}
- All operations are scoped to this contract
- When creating tasks or files, they are automatically associated with this contract"#,
        contract_context = contract_context,
        contract_id = contract_id
    );

    // Run the agentic loop
    run_contract_agentic_loop(
        pool,
        &state,
        &llm_client,
        system_prompt,
        &request,
        contract_id,
        auth.owner_id,
    )
    .await
}

fn build_contract_context(contract: &crate::db::models::ContractWithRelations) -> String {
    let c = &contract.contract;
    let mut context = format!(
        "Name: {}\nID: {}\nPhase: {}\nStatus: {}\n",
        c.name, c.id, c.phase, c.status
    );

    if let Some(ref desc) = c.description {
        context.push_str(&format!("Description: {}\n", desc));
    }

    // Build phase checklist
    let file_infos: Vec<FileInfo> = contract.files.iter().map(|f| FileInfo {
        id: f.id,
        name: f.name.clone(),
        contract_phase: f.contract_phase.clone(),
    }).collect();

    let task_infos: Vec<TaskInfo> = contract.tasks.iter().map(|t| TaskInfo {
        id: t.id,
        name: t.name.clone(),
        status: t.status.clone(),
    }).collect();

    let has_repository = !contract.repositories.is_empty();
    let phase_checklist = get_phase_checklist(&c.phase, &file_infos, &task_infos, has_repository);

    // Add phase checklist to context
    context.push_str("\n");
    context.push_str(&format_checklist_markdown(&phase_checklist));

    // Files summary
    context.push_str(&format!("\n### Files ({} total)\n", contract.files.len()));
    if !contract.files.is_empty() {
        for file in contract.files.iter().take(5) {
            let phase_label = file.contract_phase.as_deref().unwrap_or("none");
            context.push_str(&format!("- {} [{}] (ID: {})\n", file.name, phase_label, file.id));
        }
        if contract.files.len() > 5 {
            context.push_str(&format!("... and {} more\n", contract.files.len() - 5));
        }
    }

    // Tasks summary
    context.push_str(&format!("\n### Tasks ({} total)\n", contract.tasks.len()));
    if !contract.tasks.is_empty() {
        let pending = contract.tasks.iter().filter(|t| t.status == "pending").count();
        let running = contract.tasks.iter().filter(|t| t.status == "running").count();
        let done = contract.tasks.iter().filter(|t| t.status == "done").count();
        context.push_str(&format!("{} pending, {} running, {} done\n", pending, running, done));
        for task in contract.tasks.iter().take(5) {
            context.push_str(&format!("- {} ({}) - ID: {}\n", task.name, task.status, task.id));
        }
        if contract.tasks.len() > 5 {
            context.push_str(&format!("... and {} more\n", contract.tasks.len() - 5));
        }
    }

    // Repositories summary
    context.push_str(&format!("\n### Repositories ({} total)\n", contract.repositories.len()));
    if !contract.repositories.is_empty() {
        for repo in &contract.repositories {
            let primary = if repo.is_primary { " (primary)" } else { "" };
            let url_or_path = repo.repository_url.as_deref()
                .or(repo.local_path.as_deref())
                .unwrap_or("managed");
            context.push_str(&format!("- {}: {}{}\n", repo.name, url_or_path, primary));
        }
    }

    context
}

/// Summarize older conversation history to reduce token usage
async fn summarize_conversation_history(
    llm_client: &LlmClient,
    messages: &[&crate::db::models::ContractChatMessageRecord],
) -> String {
    // Build conversation text for summarization
    let mut conversation_text = String::new();
    for msg in messages {
        let role_label = if msg.role == "user" { "User" } else { "Assistant" };
        // Limit each message to avoid overwhelming the summarizer
        let content = if msg.content.len() > 500 {
            format!("{}...", &msg.content[..500])
        } else {
            msg.content.clone()
        };
        conversation_text.push_str(&format!("{}: {}\n", role_label, content));
    }

    // Limit total text to summarize
    if conversation_text.len() > 8000 {
        conversation_text = format!("{}...", &conversation_text[..8000]);
    }

    let summary_prompt = format!(
        "Summarize this conversation history in 2-3 sentences, focusing on key decisions, actions taken, and current state:\n\n{}",
        conversation_text
    );

    // Use a simple chat call without tools for summarization
    let summary = match llm_client {
        LlmClient::Claude(client) => {
            let claude_messages = vec![claude::Message {
                role: "user".to_string(),
                content: claude::MessageContent::Text(summary_prompt.clone()),
            }];
            match client.chat_with_tools(claude_messages, &[]).await {
                Ok(response) => response.content.unwrap_or_default(),
                Err(e) => {
                    tracing::warn!("Failed to summarize conversation: {}", e);
                    "Previous conversation covered contract management tasks.".to_string()
                }
            }
        }
        LlmClient::Groq(client) => {
            let groq_messages = vec![Message {
                role: "user".to_string(),
                content: Some(summary_prompt.clone()),
                tool_calls: None,
                tool_call_id: None,
            }];
            match client.chat_with_tools(groq_messages, &[]).await {
                Ok(response) => response.content.unwrap_or_default(),
                Err(e) => {
                    tracing::warn!("Failed to summarize conversation: {}", e);
                    "Previous conversation covered contract management tasks.".to_string()
                }
            }
        }
    };

    // Limit summary length
    if summary.len() > 500 {
        format!("{}...", &summary[..500])
    } else {
        summary
    }
}

/// Run the agentic loop for contract chat
async fn run_contract_agentic_loop(
    pool: &sqlx::PgPool,
    state: &SharedState,
    llm_client: &LlmClient,
    system_prompt: String,
    request: &ContractChatRequest,
    contract_id: Uuid,
    owner_id: Uuid,
) -> axum::response::Response {
    // Get or create the conversation for persistent history
    let conversation = match repository::get_or_create_contract_conversation(pool, contract_id, owner_id).await {
        Ok(conv) => conv,
        Err(e) => {
            tracing::error!("Failed to get/create contract conversation: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Failed to initialize conversation: {}", e) })),
            )
                .into_response();
        }
    };

    // Load ALL existing messages from database
    let saved_messages = match repository::list_contract_chat_messages(pool, conversation.id, None).await {
        Ok(msgs) => msgs,
        Err(e) => {
            tracing::warn!("Failed to load contract chat history: {}", e);
            Vec::new()
        }
    };

    // Build initial messages
    let mut messages = vec![Message {
        role: "system".to_string(),
        content: Some(system_prompt),
        tool_calls: None,
        tool_call_id: None,
    }];

    // Add saved conversation history, summarizing older messages if needed
    // to stay under rate limits (~25k chars ≈ ~6k tokens for history)
    const MAX_HISTORY_CHARS: usize = 25000;
    const RECENT_MESSAGES_TO_KEEP: usize = 6; // Keep last 3 turns intact

    // Filter to user/assistant messages only
    let history_messages: Vec<_> = saved_messages
        .iter()
        .filter(|m| m.role == "user" || m.role == "assistant")
        .collect();

    // Calculate total character count
    let total_chars: usize = history_messages.iter().map(|m| m.content.len()).sum();

    if total_chars > MAX_HISTORY_CHARS && history_messages.len() > RECENT_MESSAGES_TO_KEEP {
        // Need to summarize older messages
        let split_point = history_messages.len().saturating_sub(RECENT_MESSAGES_TO_KEEP);
        let older_messages = &history_messages[..split_point];
        let recent_messages = &history_messages[split_point..];

        // Generate summary of older conversation
        let summary = summarize_conversation_history(&llm_client, older_messages).await;

        // Add summary as context
        messages.push(Message {
            role: "user".to_string(),
            content: Some(format!("[Previous conversation summary: {}]", summary)),
            tool_calls: None,
            tool_call_id: None,
        });
        messages.push(Message {
            role: "assistant".to_string(),
            content: Some("I understand the previous context. Let's continue.".to_string()),
            tool_calls: None,
            tool_call_id: None,
        });

        // Add recent messages in full
        for saved_msg in recent_messages {
            messages.push(Message {
                role: saved_msg.role.clone(),
                content: Some(saved_msg.content.clone()),
                tool_calls: None,
                tool_call_id: None,
            });
        }

        tracing::info!(
            total_messages = history_messages.len(),
            summarized = older_messages.len(),
            kept_recent = recent_messages.len(),
            "Summarized older conversation history"
        );
    } else {
        // Add all messages directly
        for saved_msg in history_messages {
            messages.push(Message {
                role: saved_msg.role.clone(),
                content: Some(saved_msg.content.clone()),
                tool_calls: None,
                tool_call_id: None,
            });
        }
    }

    // Add current user message
    messages.push(Message {
        role: "user".to_string(),
        content: Some(request.message.clone()),
        tool_calls: None,
        tool_call_id: None,
    });

    // Save the user message to database
    if let Err(e) = repository::add_contract_chat_message(
        pool,
        conversation.id,
        "user",
        &request.message,
        None,
        None,
    ).await {
        tracing::warn!("Failed to save user message to contract chat history: {}", e);
    }

    // State for tracking
    let mut all_tool_call_infos: Vec<ContractToolCallInfo> = Vec::new();
    let mut final_response: Option<String> = None;
    let mut consecutive_failures = 0;
    const MAX_CONSECUTIVE_FAILURES: usize = 3;
    let mut pending_questions: Option<Vec<UserQuestion>> = None;

    // Multi-turn agentic tool calling loop
    for round in 0..MAX_TOOL_ROUNDS {
        tracing::info!(
            round = round,
            total_tool_calls = all_tool_call_infos.len(),
            "Contract agentic loop iteration"
        );

        // Check consecutive failures
        if consecutive_failures >= MAX_CONSECUTIVE_FAILURES {
            tracing::warn!(
                "Breaking contract loop due to {} consecutive failures",
                consecutive_failures
            );
            final_response = Some(
                "I encountered multiple consecutive errors and stopped. \
                Please check the contract state and try again."
                    .to_string(),
            );
            break;
        }

        // Call the appropriate LLM API
        let result = match llm_client {
            LlmClient::Groq(groq) => {
                match groq.chat_with_tools(messages.clone(), &CONTRACT_TOOLS).await {
                    Ok(r) => LlmResult {
                        content: r.content,
                        tool_calls: r.tool_calls,
                        raw_tool_calls: r.raw_tool_calls,
                        finish_reason: r.finish_reason,
                    },
                    Err(e) => {
                        tracing::error!("Groq API error: {}", e);
                        return (
                            StatusCode::INTERNAL_SERVER_ERROR,
                            Json(json!({ "error": format!("LLM API error: {}", e) })),
                        )
                            .into_response();
                    }
                }
            }
            LlmClient::Claude(claude_client) => {
                let claude_messages = claude::groq_messages_to_claude(&messages);
                match claude_client
                    .chat_with_tools(claude_messages, &CONTRACT_TOOLS)
                    .await
                {
                    Ok(r) => {
                        let raw_tool_calls: Vec<ToolCallResponse> = r
                            .tool_calls
                            .iter()
                            .map(|tc| ToolCallResponse {
                                id: tc.id.clone(),
                                call_type: "function".to_string(),
                                function: crate::llm::groq::FunctionCall {
                                    name: tc.name.clone(),
                                    arguments: tc.arguments.to_string(),
                                },
                            })
                            .collect();

                        LlmResult {
                            content: r.content,
                            tool_calls: r.tool_calls,
                            raw_tool_calls,
                            finish_reason: r.stop_reason,
                        }
                    }
                    Err(e) => {
                        tracing::error!("Claude API error: {}", e);
                        return (
                            StatusCode::INTERNAL_SERVER_ERROR,
                            Json(json!({ "error": format!("LLM API error: {}", e) })),
                        )
                            .into_response();
                    }
                }
            }
        };

        // Check if there are tool calls to execute
        if result.tool_calls.is_empty() {
            final_response = result.content;
            break;
        }

        // Add assistant message with tool calls to conversation
        messages.push(Message {
            role: "assistant".to_string(),
            content: result.content.clone(),
            tool_calls: Some(result.raw_tool_calls.clone()),
            tool_call_id: None,
        });

        // Execute each tool call
        for (i, tool_call) in result.tool_calls.iter().enumerate() {
            tracing::info!(tool = %tool_call.name, round = round, "Executing contract tool call");

            // Parse the tool call
            let mut execution_result = parse_contract_tool_call(tool_call);

            // Handle async contract tool requests
            if let Some(contract_request) = execution_result.request.take() {
                let async_result =
                    handle_contract_request(pool, &state.daemon_connections, contract_request, contract_id, owner_id).await;
                execution_result.success = async_result.success;
                execution_result.message = async_result.message;
                execution_result.data = async_result.data;
            }

            // Track consecutive failures
            if execution_result.success {
                consecutive_failures = 0;
            } else {
                consecutive_failures += 1;
                tracing::warn!(
                    tool = %tool_call.name,
                    consecutive_failures = consecutive_failures,
                    "Contract tool call failed"
                );
            }

            // Check for pending user questions
            if let Some(questions) = execution_result.pending_questions {
                tracing::info!(
                    question_count = questions.len(),
                    "Contract LLM requesting user input"
                );
                pending_questions = Some(questions);
                all_tool_call_infos.push(ContractToolCallInfo {
                    name: tool_call.name.clone(),
                    result: ToolResult {
                        success: execution_result.success,
                        message: execution_result.message.clone(),
                    },
                });
                break;
            }

            // Build tool result message
            let result_content = if let Some(data) = &execution_result.data {
                json!({
                    "success": execution_result.success,
                    "message": execution_result.message,
                    "data": data
                })
                .to_string()
            } else {
                json!({
                    "success": execution_result.success,
                    "message": execution_result.message
                })
                .to_string()
            };

            // Add tool result message
            let tool_call_id = match llm_client {
                LlmClient::Groq(_) => result.raw_tool_calls[i].id.clone(),
                LlmClient::Claude(_) => tool_call.id.clone(),
            };

            messages.push(Message {
                role: "tool".to_string(),
                content: Some(result_content),
                tool_calls: None,
                tool_call_id: Some(tool_call_id),
            });

            // Track for response
            all_tool_call_infos.push(ContractToolCallInfo {
                name: tool_call.name.clone(),
                result: ToolResult {
                    success: execution_result.success,
                    message: execution_result.message,
                },
            });
        }

        // If user questions are pending, pause
        if pending_questions.is_some() {
            final_response = result.content;
            break;
        }

        // If finish reason indicates completion, exit loop
        let finish_lower = result.finish_reason.to_lowercase();
        if finish_lower == "stop" || finish_lower == "end_turn" {
            final_response = result.content;
            break;
        }
    }

    // Build response
    let response_text = final_response.unwrap_or_else(|| {
        if all_tool_call_infos.is_empty() {
            "I couldn't understand your request. Please try rephrasing.".to_string()
        } else {
            format!(
                "Done! Executed {} tool{}.",
                all_tool_call_infos.len(),
                if all_tool_call_infos.len() == 1 { "" } else { "s" }
            )
        }
    });

    // Save assistant response to database
    let tool_calls_json = if all_tool_call_infos.is_empty() {
        None
    } else {
        serde_json::to_value(&all_tool_call_infos).ok()
    };

    let pending_questions_json = pending_questions.as_ref().and_then(|q| serde_json::to_value(q).ok());

    if let Err(e) = repository::add_contract_chat_message(
        pool,
        conversation.id,
        "assistant",
        &response_text,
        tool_calls_json,
        pending_questions_json,
    ).await {
        tracing::warn!("Failed to save assistant response to contract chat history: {}", e);
    }

    (
        StatusCode::OK,
        Json(ContractChatResponse {
            response: response_text,
            tool_calls: all_tool_call_infos,
            pending_questions,
        }),
    )
        .into_response()
}

/// Result from handling an async contract tool request
struct ContractRequestResult {
    success: bool,
    message: String,
    data: Option<serde_json::Value>,
}

/// Handle async contract tool requests that require database access
async fn handle_contract_request(
    pool: &sqlx::PgPool,
    daemon_connections: &dashmap::DashMap<String, crate::server::state::DaemonConnectionInfo>,
    request: ContractToolRequest,
    contract_id: Uuid,
    owner_id: Uuid,
) -> ContractRequestResult {
    match request {
        ContractToolRequest::ListDaemonDirectories => {
            let mut directories = Vec::new();

            // Iterate over connected daemons belonging to this owner
            for entry in daemon_connections.iter() {
                let daemon = entry.value();

                // Only include daemons belonging to this owner
                if daemon.owner_id != owner_id {
                    continue;
                }

                // Add working directory if available
                if let Some(ref working_dir) = daemon.working_directory {
                    directories.push(json!({
                        "path": working_dir,
                        "label": "Working Directory",
                        "type": "working",
                        "hostname": daemon.hostname,
                    }));
                }

                // Add home directory if available
                if let Some(ref home_dir) = daemon.home_directory {
                    directories.push(json!({
                        "path": home_dir,
                        "label": "Makima Home",
                        "type": "home",
                        "hostname": daemon.hostname,
                    }));
                }
            }

            if directories.is_empty() {
                ContractRequestResult {
                    success: true,
                    message: "No daemon directories available. Connect a daemon to get directory suggestions.".to_string(),
                    data: Some(json!({ "directories": [] })),
                }
            } else {
                ContractRequestResult {
                    success: true,
                    message: format!("Found {} suggested directories from connected daemons", directories.len()),
                    data: Some(json!({ "directories": directories })),
                }
            }
        }

        ContractToolRequest::GetContractStatus => {
            match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(cwr)) => {
                    let c = &cwr.contract;
                    ContractRequestResult {
                        success: true,
                        message: format!(
                            "Contract '{}' is in '{}' phase with status '{}'",
                            c.name, c.phase, c.status
                        ),
                        data: Some(json!({
                            "name": c.name,
                            "phase": c.phase,
                            "status": c.status,
                            "description": c.description,
                            "fileCount": cwr.files.len(),
                            "taskCount": cwr.tasks.len(),
                            "repositoryCount": cwr.repositories.len(),
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Contract not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::ListContractFiles => {
            match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(cwr)) => {
                    let files: Vec<serde_json::Value> = cwr
                        .files
                        .iter()
                        .map(|f| {
                            json!({
                                "fileId": f.id,
                                "name": f.name,
                                "description": f.description,
                                "phase": f.contract_phase,
                            })
                        })
                        .collect();

                    ContractRequestResult {
                        success: true,
                        message: format!("Found {} files", files.len()),
                        data: Some(json!({ "files": files })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Contract not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::ListContractTasks => {
            match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(cwr)) => {
                    let tasks: Vec<serde_json::Value> = cwr
                        .tasks
                        .iter()
                        .map(|t| {
                            json!({
                                "taskId": t.id,
                                "name": t.name,
                                "status": t.status,
                                "priority": t.priority,
                            })
                        })
                        .collect();

                    ContractRequestResult {
                        success: true,
                        message: format!("Found {} tasks", tasks.len()),
                        data: Some(json!({ "tasks": tasks })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Contract not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::ListContractRepositories => {
            match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(cwr)) => {
                    let repos: Vec<serde_json::Value> = cwr
                        .repositories
                        .iter()
                        .map(|r| {
                            json!({
                                "repositoryId": r.id,
                                "name": r.name,
                                "repositoryUrl": r.repository_url,
                                "localPath": r.local_path,
                                "isPrimary": r.is_primary,
                            })
                        })
                        .collect();

                    ContractRequestResult {
                        success: true,
                        message: format!("Found {} repositories", repos.len()),
                        data: Some(json!({ "repositories": repos })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Contract not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::ReadFile { file_id } => {
            match repository::get_file_for_owner(pool, file_id, owner_id).await {
                Ok(Some(file)) => {
                    // Verify file belongs to this contract
                    if file.contract_id != Some(contract_id) {
                        return ContractRequestResult {
                            success: false,
                            message: "File does not belong to this contract".to_string(),
                            data: None,
                        };
                    }

                    // Convert body to markdown for LLM consumption
                    let markdown = body_to_markdown(&file.body);

                    ContractRequestResult {
                        success: true,
                        message: format!("Read file '{}'", file.name),
                        data: Some(json!({
                            "fileId": file.id,
                            "name": file.name,
                            "description": file.description,
                            "summary": file.summary,
                            "plainText": markdown,
                            "phase": file.contract_phase,
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "File not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::CreateFileFromTemplate {
            template_id,
            name,
            description,
        } => {
            // Find the template
            let templates = all_templates();
            let template = templates.iter().find(|t| t.id == template_id);

            let Some(template) = template else {
                return ContractRequestResult {
                    success: false,
                    message: format!("Template '{}' not found", template_id),
                    data: None,
                };
            };

            // Verify contract exists and get current phase
            let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
                Ok(Some(c)) => c,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Contract not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    }
                }
            };

            // Use template's phase if available, otherwise use contract's current phase
            let contract_phase = Some(template.phase.clone()).or(Some(contract.phase.clone()));

            // Create the file (contract_id is now required)
            let create_req = crate::db::models::CreateFileRequest {
                contract_id,
                name: Some(name.clone()),
                description,
                body: template.suggested_body.clone(),
                transcript: Vec::new(),
                location: None,
                repo_file_path: None,
                contract_phase,
            };

            match repository::create_file_for_owner(pool, owner_id, create_req).await {
                Ok(file) => ContractRequestResult {
                    success: true,
                    message: format!(
                        "Created file '{}' from template '{}'",
                        name, template.name
                    ),
                    data: Some(json!({
                        "fileId": file.id,
                        "name": file.name,
                        "templateId": template_id,
                    })),
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to create file: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::CreateEmptyFile { name, description } => {
            // Verify contract exists and get current phase
            let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
                Ok(Some(c)) => c,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Contract not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    }
                }
            };

            // Create the file with current contract phase
            let create_req = crate::db::models::CreateFileRequest {
                contract_id,
                name: Some(name.clone()),
                description,
                body: Vec::new(),
                transcript: Vec::new(),
                location: None,
                repo_file_path: None,
                contract_phase: Some(contract.phase.clone()),
            };

            match repository::create_file_for_owner(pool, owner_id, create_req).await {
                Ok(file) => ContractRequestResult {
                    success: true,
                    message: format!("Created empty file '{}'", name),
                    data: Some(json!({
                        "fileId": file.id,
                        "name": file.name,
                    })),
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to create file: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::ListAvailableTemplates { phase } => {
            let templates = if let Some(p) = phase {
                templates_for_phase(&p)
            } else {
                all_templates()
            };

            let template_data: Vec<serde_json::Value> = templates
                .iter()
                .map(|t| {
                    json!({
                        "id": t.id,
                        "name": t.name,
                        "phase": t.phase,
                        "description": t.description,
                    })
                })
                .collect();

            ContractRequestResult {
                success: true,
                message: format!("Found {} templates", templates.len()),
                data: Some(json!({ "templates": template_data })),
            }
        }

        ContractToolRequest::CreateContractTask {
            name,
            plan,
            repository_url,
            base_branch,
        } => {
            // Get primary repository if not specified
            let repo_url = if repository_url.is_some() {
                repository_url
            } else {
                // Find primary repository
                match get_contract_with_relations(pool, contract_id, owner_id).await {
                    Ok(Some(contract)) => {
                        contract
                            .repositories
                            .iter()
                            .find(|r| r.is_primary)
                            .and_then(|r| r.repository_url.clone().or(r.local_path.clone()))
                    }
                    _ => None,
                }
            };

            let create_req = CreateTaskRequest {
                contract_id,
                name: name.clone(),
                description: None,
                plan,
                parent_task_id: None,
                repository_url: repo_url,
                base_branch,
                target_branch: None,
                merge_mode: None,
                priority: 0,
                target_repo_path: None,
                completion_action: None,
                continue_from_task_id: None,
                copy_files: None,
                is_supervisor: false,
                checkpoint_sha: None,
            };

            match repository::create_task_for_owner(pool, owner_id, create_req).await {
                Ok(task) => ContractRequestResult {
                    success: true,
                    message: format!("Created task '{}' in contract", name),
                    data: Some(json!({
                        "taskId": task.id,
                        "name": task.name,
                        "status": task.status,
                    })),
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to create task: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::DelegateContentGeneration {
            file_id,
            instruction,
            context,
        } => {
            // Build a task plan that includes the content generation instruction
            let mut plan = format!(
                "Content Generation Task\n\n\
                ## Instruction\n{}\n\n",
                instruction
            );

            if let Some(ctx) = context {
                plan.push_str(&format!("## Context\n{}\n\n", ctx));
            }

            // If file_id is provided, get file details and include them
            let (file_name, file_info) = if let Some(fid) = file_id {
                match repository::get_file_for_owner(pool, fid, owner_id).await {
                    Ok(Some(file)) => {
                        let info = format!(
                            "## Target File\n\
                            - File ID: {}\n\
                            - Name: {}\n\
                            - Description: {}\n\n\
                            The generated content should be structured to update this file.\n",
                            fid,
                            file.name,
                            file.description.as_deref().unwrap_or("(no description)")
                        );
                        (Some(file.name.clone()), Some(info))
                    }
                    _ => (None, None),
                }
            } else {
                (None, None)
            };

            if let Some(info) = file_info {
                plan.push_str(&info);
            }

            // Get primary repository
            let repo_url = match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(contract)) => contract
                    .repositories
                    .iter()
                    .find(|r| r.is_primary)
                    .and_then(|r| r.repository_url.clone().or(r.local_path.clone())),
                _ => None,
            };

            let task_name = format!(
                "Generate content{}",
                file_name.map(|n| format!(": {}", n)).unwrap_or_default()
            );

            let create_req = CreateTaskRequest {
                contract_id,
                name: task_name.clone(),
                description: Some(instruction.clone()),
                plan,
                parent_task_id: None,
                repository_url: repo_url,
                base_branch: None,
                target_branch: None,
                merge_mode: None,
                priority: 0,
                target_repo_path: None,
                completion_action: None,
                continue_from_task_id: None,
                copy_files: None,
                is_supervisor: false,
                checkpoint_sha: None,
            };

            match repository::create_task_for_owner(pool, owner_id, create_req).await {
                Ok(task) => ContractRequestResult {
                    success: true,
                    message: format!(
                        "Created content generation task '{}'. Start the task to generate the content.",
                        task_name
                    ),
                    data: Some(json!({
                        "taskId": task.id,
                        "name": task.name,
                        "status": task.status,
                        "targetFileId": file_id,
                    })),
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to create content generation task: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::StartTask { task_id } => {
            // Get the task
            let task = match repository::get_task_for_owner(pool, task_id, owner_id).await {
                Ok(Some(t)) => t,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Task not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Failed to get task: {}", e),
                        data: None,
                    }
                }
            };

            // Check if task can be started
            let startable_statuses = ["pending", "failed", "interrupted", "done", "merged"];
            if !startable_statuses.contains(&task.status.as_str()) {
                return ContractRequestResult {
                    success: false,
                    message: format!("Task cannot be started from status: {}", task.status),
                    data: None,
                };
            }

            // Find a connected daemon for this owner
            let daemon_entry = daemon_connections
                .iter()
                .find(|d| d.value().owner_id == owner_id);

            let (target_daemon_id, command_sender) = match daemon_entry {
                Some(entry) => {
                    let daemon = entry.value();
                    (daemon.id, daemon.command_sender.clone())
                }
                None => {
                    return ContractRequestResult {
                        success: false,
                        message: "No daemon connected. Start a daemon to run tasks.".to_string(),
                        data: None,
                    };
                }
            };

            // Check if this is an orchestrator
            let subtask_count = match repository::list_subtasks_for_owner(pool, task_id, owner_id).await {
                Ok(subtasks) => subtasks.len(),
                Err(_) => 0,
            };
            let is_orchestrator = task.depth == 0 && subtask_count > 0;

            // Update task status to 'starting' and assign daemon_id
            let update_req = crate::db::models::UpdateTaskRequest {
                status: Some("starting".to_string()),
                daemon_id: Some(target_daemon_id),
                version: Some(task.version),
                ..Default::default()
            };

            let _updated_task = match repository::update_task_for_owner(pool, task_id, owner_id, update_req).await {
                Ok(Some(t)) => t,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Task not found".to_string(),
                        data: None,
                    };
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Failed to update task: {}", e),
                        data: None,
                    };
                }
            };

            // Send SpawnTask command to daemon
            let command = DaemonCommand::SpawnTask {
                task_id,
                task_name: task.name.clone(),
                plan: task.plan.clone(),
                repo_url: task.repository_url.clone(),
                base_branch: task.base_branch.clone(),
                target_branch: task.target_branch.clone(),
                parent_task_id: task.parent_task_id,
                depth: task.depth,
                is_orchestrator,
                target_repo_path: task.target_repo_path.clone(),
                completion_action: task.completion_action.clone(),
                continue_from_task_id: task.continue_from_task_id,
                copy_files: task.copy_files.as_ref().and_then(|v| serde_json::from_value(v.clone()).ok()),
                contract_id: task.contract_id,
                is_supervisor: task.is_supervisor,
            };

            if let Err(e) = command_sender.send(command).await {
                // Rollback: reset status since command failed
                let rollback_req = crate::db::models::UpdateTaskRequest {
                    status: Some("pending".to_string()),
                    clear_daemon_id: true,
                    ..Default::default()
                };
                let _ = repository::update_task_for_owner(pool, task_id, owner_id, rollback_req).await;
                return ContractRequestResult {
                    success: false,
                    message: format!("Failed to send task to daemon: {}", e),
                    data: None,
                };
            }

            // Note: TaskUpdateNotification broadcast is handled by the mesh handler when daemon reports status
            ContractRequestResult {
                success: true,
                message: format!("Started task '{}'. The task is now running on a connected daemon.", task.name),
                data: Some(json!({
                    "taskId": task_id,
                    "name": task.name,
                    "status": "starting",
                })),
            }
        }

        ContractToolRequest::GetPhaseInfo => {
            let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
                Ok(Some(c)) => c,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Contract not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    }
                }
            };

            let phase_info = get_phase_description(&contract.phase);
            let templates = templates_for_phase(&contract.phase);
            let template_names: Vec<String> = templates.iter().map(|t| t.name.clone()).collect();

            ContractRequestResult {
                success: true,
                message: format!("Contract is in '{}' phase", contract.phase),
                data: Some(json!({
                    "phase": contract.phase,
                    "description": phase_info.0,
                    "activities": phase_info.1,
                    "suggestedTemplates": template_names,
                    "nextPhase": get_next_phase(&contract.phase),
                })),
            }
        }

        ContractToolRequest::SuggestPhaseTransition => {
            let contract = match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(c)) => c,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Contract not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    }
                }
            };

            let analysis = analyze_phase_readiness(&contract);

            ContractRequestResult {
                success: true,
                message: analysis.summary.clone(),
                data: Some(json!({
                    "currentPhase": contract.contract.phase,
                    "nextPhase": get_next_phase(&contract.contract.phase),
                    "ready": analysis.ready,
                    "summary": analysis.summary,
                    "reasons": analysis.reasons,
                    "suggestions": analysis.suggestions,
                })),
            }
        }

        ContractToolRequest::AdvancePhase { new_phase, confirmed, feedback } => {
            let contract = match repository::get_contract_for_owner(pool, contract_id, owner_id).await {
                Ok(Some(c)) => c,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Contract not found".to_string(),
                        data: None,
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    }
                }
            };

            // Validate phase transition
            let current_phase = &contract.phase;
            let valid_next = get_next_phase(current_phase);

            if valid_next.as_deref() != Some(&new_phase) {
                return ContractRequestResult {
                    success: false,
                    message: format!(
                        "Cannot transition from '{}' to '{}'. Next valid phase is: {:?}",
                        current_phase, new_phase, valid_next
                    ),
                    data: None,
                };
            }

            // Check if phase_guard is enabled
            if contract.phase_guard {
                // If user provided feedback, return it for the task to address
                if let Some(ref user_feedback) = feedback {
                    return ContractRequestResult {
                        success: true,
                        message: format!(
                            "Phase transition to '{}' requires changes. User feedback: {}",
                            new_phase, user_feedback
                        ),
                        data: Some(json!({
                            "status": "changes_requested",
                            "currentPhase": current_phase,
                            "requestedPhase": new_phase,
                            "feedback": user_feedback,
                            "action": "Address the user feedback and try again when ready"
                        })),
                    };
                }

                // If not confirmed, return pending confirmation with phase deliverables
                if !confirmed {
                    // Get files created in this phase
                    let phase_files = match repository::list_files_in_contract(pool, contract_id, owner_id).await {
                        Ok(files) => files
                            .into_iter()
                            .filter(|f| f.contract_phase.as_deref() == Some(current_phase))
                            .map(|f| json!({
                                "id": f.id,
                                "name": f.name,
                                "description": f.description
                            }))
                            .collect::<Vec<_>>(),
                        Err(_) => Vec::new(),
                    };

                    // Get tasks completed in this contract
                    let phase_tasks = match repository::list_tasks_in_contract(pool, contract_id, owner_id).await {
                        Ok(tasks) => tasks
                            .into_iter()
                            .filter(|t| t.status == "done" || t.status == "completed")
                            .map(|t| json!({
                                "id": t.id,
                                "name": t.name,
                                "status": t.status
                            }))
                            .collect::<Vec<_>>(),
                        Err(_) => Vec::new(),
                    };

                    // Build deliverables summary
                    let deliverables_summary = format!(
                        "Phase '{}' deliverables: {} files created, {} tasks completed.",
                        current_phase,
                        phase_files.len(),
                        phase_tasks.len()
                    );

                    let transition_id = uuid::Uuid::new_v4().to_string();

                    return ContractRequestResult {
                        success: true,
                        message: format!(
                            "Phase transition to '{}' requires user confirmation. Review the deliverables and call advance_phase again with confirmed=true to proceed, or provide feedback to request changes.",
                            new_phase
                        ),
                        data: Some(json!({
                            "status": "pending_confirmation",
                            "transitionId": transition_id,
                            "currentPhase": current_phase,
                            "nextPhase": new_phase,
                            "deliverablesSummary": deliverables_summary,
                            "phaseFiles": phase_files,
                            "phaseTasks": phase_tasks,
                            "requiresConfirmation": true,
                            "instructions": "To proceed: call advance_phase with confirmed=true. To request changes: call advance_phase with feedback='your feedback here'"
                        })),
                    };
                }
            }

            // Update phase (either phase_guard is disabled, or user confirmed)
            match repository::change_contract_phase_for_owner(pool, contract_id, owner_id, &new_phase).await {
                Ok(Some(updated)) => {
                    // Get deliverables for the new phase
                    let deliverables = crate::llm::get_phase_deliverables(&new_phase);

                    // Build suggested files list
                    let suggested_files: Vec<serde_json::Value> = deliverables
                        .recommended_files
                        .iter()
                        .map(|f| json!({
                            "templateId": f.template_id,
                            "name": f.name_suggestion,
                            "priority": format!("{:?}", f.priority).to_lowercase(),
                            "description": f.description,
                        }))
                        .collect();

                    ContractRequestResult {
                        success: true,
                        message: format!(
                            "Advanced contract from '{}' to '{}' phase. {}",
                            current_phase, new_phase, deliverables.guidance
                        ),
                        data: Some(json!({
                            "status": "advanced",
                            "previousPhase": current_phase,
                            "newPhase": updated.phase,
                            "phaseGuidance": deliverables.guidance,
                            "suggestedFiles": suggested_files,
                            "requiresRepository": deliverables.requires_repository,
                            "requiresTasks": deliverables.requires_tasks,
                        })),
                    }
                },
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Failed to update phase".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to update phase: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::AddRepository {
            repo_type,
            name,
            url,
            is_primary,
        } => {
            let add_result = match repo_type.as_str() {
                "remote" => {
                    let url = url.unwrap_or_default();
                    repository::add_remote_repository(
                        pool,
                        contract_id,
                        &name,
                        &url,
                        is_primary,
                    )
                    .await
                }
                "local" => {
                    let path = url.unwrap_or_default();
                    repository::add_local_repository(
                        pool,
                        contract_id,
                        &name,
                        &path,
                        is_primary,
                    )
                    .await
                }
                "managed" => {
                    repository::create_managed_repository(pool, contract_id, &name, is_primary)
                        .await
                }
                _ => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Invalid repository type: {}", repo_type),
                        data: None,
                    }
                }
            };

            match add_result {
                Ok(repo) => ContractRequestResult {
                    success: true,
                    message: format!("Added {} repository '{}'", repo_type, name),
                    data: Some(json!({
                        "repositoryId": repo.id,
                        "name": repo.name,
                        "isPrimary": repo.is_primary,
                    })),
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to add repository: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::SetPrimaryRepository { repository_id } => {
            match repository::set_repository_primary(pool, repository_id, contract_id).await {
                Ok(true) => ContractRequestResult {
                    success: true,
                    message: "Set repository as primary".to_string(),
                    data: Some(json!({
                        "repositoryId": repository_id,
                    })),
                },
                Ok(false) => ContractRequestResult {
                    success: false,
                    message: "Repository not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Failed to set primary repository: {}", e),
                    data: None,
                },
            }
        }

        // =============================================================================
        // Phase Guidance Handlers
        // =============================================================================

        ContractToolRequest::GetPhaseChecklist => {
            match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(cwr)) => {
                    let file_infos: Vec<FileInfo> = cwr.files.iter().map(|f| FileInfo {
                        id: f.id,
                        name: f.name.clone(),
                        contract_phase: f.contract_phase.clone(),
                    }).collect();

                    let task_infos: Vec<TaskInfo> = cwr.tasks.iter().map(|t| TaskInfo {
                        id: t.id,
                        name: t.name.clone(),
                        status: t.status.clone(),
                    }).collect();

                    let has_repository = !cwr.repositories.is_empty();
                    let checklist = get_phase_checklist(&cwr.contract.phase, &file_infos, &task_infos, has_repository);

                    ContractRequestResult {
                        success: true,
                        message: checklist.summary.clone(),
                        data: Some(json!({
                            "phase": checklist.phase,
                            "completionPercentage": checklist.completion_percentage,
                            "deliverables": checklist.file_deliverables,
                            "hasRepository": checklist.has_repository,
                            "repositoryRequired": checklist.repository_required,
                            "taskStats": checklist.task_stats,
                            "suggestions": checklist.suggestions,
                            "summary": checklist.summary,
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Contract not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        // =============================================================================
        // Task Derivation Handlers
        // =============================================================================

        ContractToolRequest::DeriveTasksFromFile { file_id } => {
            // First get the file
            match repository::get_file_for_owner(pool, file_id, owner_id).await {
                Ok(Some(file)) => {
                    // Verify file belongs to this contract
                    if file.contract_id != Some(contract_id) {
                        return ContractRequestResult {
                            success: false,
                            message: "File does not belong to this contract".to_string(),
                            data: None,
                        };
                    }

                    // Convert body to markdown for task parsing
                    let markdown = body_to_markdown(&file.body);

                    // Parse tasks from the content
                    let parse_result = parse_tasks_from_breakdown(&markdown);

                    ContractRequestResult {
                        success: true,
                        message: format!("Found {} tasks in file '{}'", parse_result.total, file.name),
                        data: Some(json!({
                            "fileId": file_id,
                            "fileName": file.name,
                            "tasks": parse_result.tasks,
                            "groups": parse_result.groups,
                            "total": parse_result.total,
                            "warnings": parse_result.warnings,
                            "formatted": format_parsed_tasks(&parse_result),
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "File not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::CreateChainedTasks { tasks } => {
            // Get primary repository for tasks
            let repo_url = match get_contract_with_relations(pool, contract_id, owner_id).await {
                Ok(Some(contract)) => {
                    contract
                        .repositories
                        .iter()
                        .find(|r| r.is_primary)
                        .and_then(|r| r.repository_url.clone().or(r.local_path.clone()))
                }
                _ => None,
            };

            let mut created_tasks = Vec::new();
            let mut previous_task_id: Option<Uuid> = None;

            for task_def in &tasks {
                let create_req = CreateTaskRequest {
                    contract_id,
                    name: task_def.name.clone(),
                    description: None,
                    plan: task_def.plan.clone(),
                    parent_task_id: None,
                    repository_url: repo_url.clone(),
                    base_branch: None,
                    target_branch: None,
                    merge_mode: None,
                    priority: 0,
                    target_repo_path: None,
                    completion_action: None,
                    continue_from_task_id: previous_task_id,
                    copy_files: None,
                    is_supervisor: false,
                    checkpoint_sha: None,
                };

                match repository::create_task_for_owner(pool, owner_id, create_req).await {
                    Ok(task) => {
                        created_tasks.push(json!({
                            "taskId": task.id,
                            "name": task.name,
                            "status": task.status,
                            "chainedFrom": previous_task_id,
                        }));
                        previous_task_id = Some(task.id);
                    }
                    Err(e) => {
                        return ContractRequestResult {
                            success: false,
                            message: format!("Failed to create task '{}': {}", task_def.name, e),
                            data: Some(json!({
                                "createdSoFar": created_tasks,
                            })),
                        };
                    }
                }
            }

            ContractRequestResult {
                success: true,
                message: format!("Created {} chained tasks", created_tasks.len()),
                data: Some(json!({
                    "tasks": created_tasks,
                    "total": created_tasks.len(),
                })),
            }
        }

        // =============================================================================
        // Task Completion Processing Handlers
        // =============================================================================

        ContractToolRequest::ProcessTaskCompletion { task_id } => {
            // Get the task
            match repository::get_task_for_owner(pool, task_id, owner_id).await {
                Ok(Some(task)) => {
                    // Verify task belongs to this contract
                    if task.contract_id != Some(contract_id) {
                        return ContractRequestResult {
                            success: false,
                            message: "Task does not belong to this contract".to_string(),
                            data: None,
                        };
                    }

                    // Get contract for context
                    let contract = get_contract_with_relations(pool, contract_id, owner_id).await.ok().flatten();

                    let total_tasks = contract.as_ref().map(|c| c.tasks.len()).unwrap_or(0);
                    let completed_tasks = contract.as_ref()
                        .map(|c| c.tasks.iter().filter(|t| t.status == "done").count())
                        .unwrap_or(0);

                    // Note: Finding next chained task would require querying full Task objects
                    // Since TaskSummary doesn't have continue_from_task_id, we skip this for now
                    let next_task: Option<(Uuid, String)> = None;

                    // Find Dev Notes file if exists
                    let dev_notes = if let Some(ref c) = contract {
                        c.files.iter()
                            .find(|f| f.name.to_lowercase().contains("dev") && f.name.to_lowercase().contains("notes"))
                            .map(|f| (f.id, f.name.clone()))
                    } else {
                        None
                    };

                    let contract_phase = contract.as_ref()
                        .map(|c| c.contract.phase.clone())
                        .unwrap_or_else(|| "execute".to_string());

                    // Analyze the task output
                    let analysis = analyze_task_output(
                        task_id,
                        &task.name,
                        task.last_output.as_deref(),
                        task.progress_summary.as_deref(),
                        &contract_phase,
                        total_tasks,
                        completed_tasks,
                        next_task,
                        dev_notes,
                    );

                    ContractRequestResult {
                        success: true,
                        message: format!("Analyzed completion of task '{}'", task.name),
                        data: Some(json!({
                            "taskId": task_id,
                            "taskName": task.name,
                            "taskStatus": task.status,
                            "summary": analysis.summary,
                            "filesAffected": analysis.files_affected,
                            "nextSteps": analysis.next_steps,
                            "phaseImpact": analysis.phase_impact,
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Task not found".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        ContractToolRequest::UpdateFileFromTask { file_id, task_id, section_title } => {
            // Get the task
            let task = match repository::get_task_for_owner(pool, task_id, owner_id).await {
                Ok(Some(t)) => t,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "Task not found".to_string(),
                        data: None,
                    };
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    };
                }
            };

            // Get the file
            let file = match repository::get_file_for_owner(pool, file_id, owner_id).await {
                Ok(Some(f)) => f,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "File not found".to_string(),
                        data: None,
                    };
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    };
                }
            };

            // Verify file belongs to this contract
            if file.contract_id != Some(contract_id) {
                return ContractRequestResult {
                    success: false,
                    message: "File does not belong to this contract".to_string(),
                    data: None,
                };
            }

            // Build the section to add
            let title = section_title.unwrap_or_else(|| format!("Task: {}", task.name));
            let result_text = task.last_output.as_deref().unwrap_or("Task completed");

            // Create new body elements to append
            let mut new_body = file.body.clone();
            new_body.push(crate::db::models::BodyElement::Heading {
                level: 2,
                text: title,
            });
            new_body.push(crate::db::models::BodyElement::Paragraph {
                text: format!("Status: {}", task.status),
            });
            new_body.push(crate::db::models::BodyElement::Paragraph {
                text: result_text.to_string(),
            });

            // Update the file using UpdateFileRequest
            let update_req = UpdateFileRequest {
                name: None,
                description: None,
                transcript: None,
                summary: None,
                body: Some(new_body),
                version: None, // Don't require version for this update
                repo_file_path: None,
            };

            match repository::update_file_for_owner(pool, file_id, owner_id, update_req).await {
                Ok(Some(updated_file)) => {
                    ContractRequestResult {
                        success: true,
                        message: format!("Updated file '{}' with task summary", file.name),
                        data: Some(json!({
                            "fileId": file_id,
                            "fileName": updated_file.name,
                            "taskId": task_id,
                            "taskName": task.name,
                        })),
                    }
                }
                Ok(None) => ContractRequestResult {
                    success: false,
                    message: "Failed to update file".to_string(),
                    data: None,
                },
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Database error: {}", e),
                    data: None,
                },
            }
        }

        // =============================================================================
        // Transcript Analysis Handlers
        // =============================================================================

        ContractToolRequest::AnalyzeTranscript { file_id } => {
            // Get the file
            let file = match repository::get_file_for_owner(pool, file_id, owner_id).await {
                Ok(Some(f)) => f,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "File not found".to_string(),
                        data: None,
                    };
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    };
                }
            };

            if file.transcript.is_empty() {
                return ContractRequestResult {
                    success: false,
                    message: "File has no transcript to analyze".to_string(),
                    data: None,
                };
            }

            // Format and analyze
            let transcript_text = format_transcript_for_analysis(&file.transcript);
            let speaker_stats = calculate_speaker_stats(&file.transcript);
            let prompt = build_analysis_prompt(&transcript_text);

            // Call Claude for analysis
            let client = match ClaudeClient::from_env(ClaudeModel::Sonnet) {
                Ok(c) => c,
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Failed to create Claude client: {}", e),
                        data: None,
                    };
                }
            };

            let claude_messages = vec![claude::Message {
                role: "user".to_string(),
                content: claude::MessageContent::Text(prompt),
            }];

            match client.chat_with_tools(claude_messages, &[]).await {
                Ok(result) => {
                    let response_content = result.content.unwrap_or_default();
                    match parse_analysis_response(&response_content, speaker_stats) {
                        Ok(analysis) => {
                            ContractRequestResult {
                                success: true,
                                message: format!(
                                    "Analysis complete: {} requirements, {} decisions, {} action items",
                                    analysis.requirements.len(),
                                    analysis.decisions.len(),
                                    analysis.action_items.len()
                                ),
                                data: Some(json!({
                                    "analysis": analysis
                                })),
                            }
                        }
                        Err(e) => ContractRequestResult {
                            success: false,
                            message: format!("Failed to parse analysis: {}", e),
                            data: None,
                        }
                    }
                }
                Err(e) => ContractRequestResult {
                    success: false,
                    message: format!("Claude API error: {}", e),
                    data: None,
                }
            }
        }

        ContractToolRequest::CreateContractFromTranscript {
            file_id, name, description, include_requirements, include_decisions, include_action_items
        } => {
            // Get file
            let file = match repository::get_file_for_owner(pool, file_id, owner_id).await {
                Ok(Some(f)) => f,
                Ok(None) => {
                    return ContractRequestResult {
                        success: false,
                        message: "File not found".to_string(),
                        data: None,
                    };
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Database error: {}", e),
                        data: None,
                    };
                }
            };

            if file.transcript.is_empty() {
                return ContractRequestResult {
                    success: false,
                    message: "File has no transcript".to_string(),
                    data: None,
                };
            }

            // Analyze transcript
            let transcript_text = format_transcript_for_analysis(&file.transcript);
            let speaker_stats = calculate_speaker_stats(&file.transcript);
            let prompt = build_analysis_prompt(&transcript_text);

            let client = match ClaudeClient::from_env(ClaudeModel::Sonnet) {
                Ok(c) => c,
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Failed to create Claude client: {}", e),
                        data: None,
                    };
                }
            };

            let claude_messages = vec![claude::Message {
                role: "user".to_string(),
                content: claude::MessageContent::Text(prompt),
            }];

            let analysis = match client.chat_with_tools(claude_messages, &[]).await {
                Ok(result) => {
                    let response_content = result.content.unwrap_or_default();
                    match parse_analysis_response(&response_content, speaker_stats) {
                        Ok(a) => a,
                        Err(e) => {
                            return ContractRequestResult {
                                success: false,
                                message: format!("Failed to parse analysis: {}", e),
                                data: None,
                            };
                        }
                    }
                }
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Claude API error: {}", e),
                        data: None,
                    };
                }
            };

            // Create contract
            let contract_name = name
                .or(analysis.suggested_contract_name.clone())
                .unwrap_or_else(|| format!("Contract from {}", file.name));
            let contract_description = description.or(analysis.suggested_description.clone());

            let contract_req = crate::db::models::CreateContractRequest {
                name: contract_name.clone(),
                description: contract_description,
                contract_type: Some("specification".to_string()),
                initial_phase: Some("research".to_string()),
                autonomous_loop: None,
                phase_guard: None,
            };

            let contract = match repository::create_contract_for_owner(pool, owner_id, contract_req).await {
                Ok(c) => c,
                Err(e) => {
                    return ContractRequestResult {
                        success: false,
                        message: format!("Failed to create contract: {}", e),
                        data: None,
                    };
                }
            };

            let mut files_created = 0;
            let mut tasks_created = 0;

            // Create requirements file if requested and there are requirements
            if include_requirements && !analysis.requirements.is_empty() {
                let requirements_items: Vec<String> = analysis.requirements
                    .iter()
                    .map(|req| format!("[{}] {}", req.speaker, req.text))
                    .collect();

                let body: Vec<crate::db::models::BodyElement> = vec![
                    crate::db::models::BodyElement::Heading {
                        level: 1,
                        text: "Requirements".to_string(),
                    },
                    crate::db::models::BodyElement::Paragraph {
                        text: format!("Extracted {} requirements from transcript analysis.", analysis.requirements.len()),
                    },
                    crate::db::models::BodyElement::Heading {
                        level: 2,
                        text: "Extracted Requirements".to_string(),
                    },
                    crate::db::models::BodyElement::List {
                        ordered: false,
                        items: requirements_items,
                    },
                ];

                let create_req = crate::db::models::CreateFileRequest {
                    contract_id: contract.id,
                    name: Some("Requirements".to_string()),
                    description: Some("Requirements extracted from transcript analysis".to_string()),
                    body,
                    transcript: Vec::new(),
                    location: None,
                    repo_file_path: None,
                    contract_phase: Some("specify".to_string()),
                };

                if repository::create_file_for_owner(pool, owner_id, create_req).await.is_ok() {
                    files_created += 1;
                }
            }

            // Create decisions file if requested and there are decisions
            if include_decisions && !analysis.decisions.is_empty() {
                let decisions_items: Vec<String> = analysis.decisions
                    .iter()
                    .map(|dec| format!("[{}] {}", dec.speaker, dec.text))
                    .collect();

                let body: Vec<crate::db::models::BodyElement> = vec![
                    crate::db::models::BodyElement::Heading {
                        level: 1,
                        text: "Decisions".to_string(),
                    },
                    crate::db::models::BodyElement::Paragraph {
                        text: format!("Extracted {} decisions from transcript analysis.", analysis.decisions.len()),
                    },
                    crate::db::models::BodyElement::Heading {
                        level: 2,
                        text: "Recorded Decisions".to_string(),
                    },
                    crate::db::models::BodyElement::List {
                        ordered: false,
                        items: decisions_items,
                    },
                ];

                let create_req = crate::db::models::CreateFileRequest {
                    contract_id: contract.id,
                    name: Some("Decisions".to_string()),
                    description: Some("Decisions extracted from transcript analysis".to_string()),
                    body,
                    transcript: Vec::new(),
                    location: None,
                    repo_file_path: None,
                    contract_phase: Some("research".to_string()),
                };

                if repository::create_file_for_owner(pool, owner_id, create_req).await.is_ok() {
                    files_created += 1;
                }
            }

            // Create tasks from action items if requested
            if include_action_items && !analysis.action_items.is_empty() {
                for item in &analysis.action_items {
                    let task_req = CreateTaskRequest {
                        contract_id: contract.id,
                        name: item.text.chars().take(100).collect(),
                        description: Some(format!("Action item from: {}", item.speaker)),
                        plan: item.text.clone(),
                        parent_task_id: None,
                        repository_url: None,
                        base_branch: None,
                        target_branch: None,
                        merge_mode: None,
                        priority: match item.priority.as_deref() {
                            Some("high") => 10,
                            Some("medium") => 5,
                            _ => 0,
                        },
                        target_repo_path: None,
                        completion_action: None,
                        continue_from_task_id: None,
                        copy_files: None,
                        is_supervisor: false,
                        checkpoint_sha: None,
                    };

                    if repository::create_task_for_owner(pool, owner_id, task_req).await.is_ok() {
                        tasks_created += 1;
                    }
                }
            }

            ContractRequestResult {
                success: true,
                message: format!(
                    "Created contract '{}' with {} files and {} tasks from transcript analysis",
                    contract_name, files_created, tasks_created
                ),
                data: Some(json!({
                    "contractId": contract.id,
                    "contractName": contract_name,
                    "filesCreated": files_created,
                    "tasksCreated": tasks_created,
                    "analysis": {
                        "requirementsCount": analysis.requirements.len(),
                        "decisionsCount": analysis.decisions.len(),
                        "actionItemsCount": analysis.action_items.len()
                    }
                })),
            }
        }
    }
}

/// Get description and activities for a phase
fn get_phase_description(phase: &str) -> (String, Vec<String>) {
    match phase {
        "research" => (
            "Gather information, analyze competitors, and understand user needs".to_string(),
            vec![
                "Conduct user research".to_string(),
                "Analyze competitors".to_string(),
                "Document findings".to_string(),
                "Identify opportunities".to_string(),
            ],
        ),
        "specify" => (
            "Define requirements, user stories, and acceptance criteria".to_string(),
            vec![
                "Write requirements".to_string(),
                "Create user stories".to_string(),
                "Define acceptance criteria".to_string(),
                "Document constraints".to_string(),
            ],
        ),
        "plan" => (
            "Design architecture, create task breakdowns, and technical designs".to_string(),
            vec![
                "Design system architecture".to_string(),
                "Create technical specifications".to_string(),
                "Break down into tasks".to_string(),
                "Plan implementation order".to_string(),
            ],
        ),
        "execute" => (
            "Implement features, write code, and run tasks".to_string(),
            vec![
                "Implement features".to_string(),
                "Write tests".to_string(),
                "Track progress".to_string(),
                "Document implementation details".to_string(),
            ],
        ),
        "review" => (
            "Review work, create release notes, and conduct retrospectives".to_string(),
            vec![
                "Review code and features".to_string(),
                "Create release notes".to_string(),
                "Conduct retrospective".to_string(),
                "Document learnings".to_string(),
            ],
        ),
        _ => (
            "Unknown phase".to_string(),
            vec![],
        ),
    }
}

/// Get the next phase in the lifecycle
fn get_next_phase(current: &str) -> Option<String> {
    match current {
        "research" => Some("specify".to_string()),
        "specify" => Some("plan".to_string()),
        "plan" => Some("execute".to_string()),
        "execute" => Some("review".to_string()),
        "review" => None, // Final phase
        _ => None,
    }
}

/// Phase readiness analysis result
struct PhaseReadinessAnalysis {
    ready: bool,
    summary: String,
    reasons: Vec<String>,
    suggestions: Vec<String>,
}

/// Analyze if the contract is ready to transition to the next phase
fn analyze_phase_readiness(contract: &crate::db::models::ContractWithRelations) -> PhaseReadinessAnalysis {
    let mut reasons = Vec::new();
    let mut suggestions = Vec::new();

    match contract.contract.phase.as_str() {
        "research" => {
            // Check for research files
            let research_files = contract.files.iter()
                .filter(|f| f.contract_phase.as_deref() == Some("research"))
                .count();

            if research_files == 0 {
                reasons.push("No research documents created yet".to_string());
                suggestions.push("Create research notes or competitor analysis documents".to_string());
            } else {
                reasons.push(format!("{} research document(s) created", research_files));
            }

            let ready = research_files > 0;
            PhaseReadinessAnalysis {
                ready,
                summary: if ready {
                    "Research phase has documentation. Consider transitioning to Specify phase.".to_string()
                } else {
                    "Research phase needs more documentation before transitioning.".to_string()
                },
                reasons,
                suggestions,
            }
        }
        "specify" => {
            let spec_files = contract.files.iter()
                .filter(|f| f.contract_phase.as_deref() == Some("specify"))
                .count();

            if spec_files == 0 {
                reasons.push("No specification documents created yet".to_string());
                suggestions.push("Create requirements or user stories documents".to_string());
            } else {
                reasons.push(format!("{} specification document(s) created", spec_files));
            }

            let ready = spec_files > 0;
            PhaseReadinessAnalysis {
                ready,
                summary: if ready {
                    "Specification phase has documentation. Consider transitioning to Plan phase.".to_string()
                } else {
                    "Specification phase needs requirements or user stories.".to_string()
                },
                reasons,
                suggestions,
            }
        }
        "plan" => {
            let plan_files = contract.files.iter()
                .filter(|f| f.contract_phase.as_deref() == Some("plan"))
                .count();

            let has_repos = !contract.repositories.is_empty();

            if plan_files == 0 {
                reasons.push("No planning documents created yet".to_string());
                suggestions.push("Create architecture or task breakdown documents".to_string());
            } else {
                reasons.push(format!("{} planning document(s) created", plan_files));
            }

            if !has_repos {
                reasons.push("No repositories configured".to_string());
                suggestions.push("Add a repository for task execution".to_string());
            } else {
                reasons.push(format!("{} repository(ies) configured", contract.repositories.len()));
            }

            let ready = plan_files > 0 && has_repos;
            PhaseReadinessAnalysis {
                ready,
                summary: if ready {
                    "Planning phase complete with documents and repositories. Ready for Execute phase.".to_string()
                } else {
                    "Planning phase needs documentation and/or repository configuration.".to_string()
                },
                reasons,
                suggestions,
            }
        }
        "execute" => {
            let total_tasks = contract.tasks.len();
            let done_tasks = contract.tasks.iter().filter(|t| t.status == "done").count();
            let running_tasks = contract.tasks.iter().filter(|t| t.status == "running").count();

            if total_tasks == 0 {
                reasons.push("No tasks created yet".to_string());
                suggestions.push("Create tasks to implement the planned work".to_string());
            } else {
                reasons.push(format!("{} of {} tasks completed", done_tasks, total_tasks));
            }

            if running_tasks > 0 {
                reasons.push(format!("{} task(s) still running", running_tasks));
                suggestions.push("Wait for running tasks to complete".to_string());
            }

            let ready = total_tasks > 0 && done_tasks == total_tasks;
            PhaseReadinessAnalysis {
                ready,
                summary: if ready {
                    "All tasks completed. Ready for Review phase.".to_string()
                } else if total_tasks == 0 {
                    "No tasks created yet. Create and complete tasks before reviewing.".to_string()
                } else {
                    format!("{}/{} tasks complete. Finish remaining tasks before review.", done_tasks, total_tasks)
                },
                reasons,
                suggestions,
            }
        }
        "review" => {
            let review_files = contract.files.iter()
                .filter(|f| f.contract_phase.as_deref() == Some("review"))
                .count();

            if review_files == 0 {
                suggestions.push("Create review checklist or release notes".to_string());
            }

            PhaseReadinessAnalysis {
                ready: false,
                summary: "Review is the final phase. Contract can be marked as complete when review is done.".to_string(),
                reasons: vec!["Review phase is the final phase".to_string()],
                suggestions,
            }
        }
        _ => PhaseReadinessAnalysis {
            ready: false,
            summary: "Unknown phase".to_string(),
            reasons: vec!["Phase not recognized".to_string()],
            suggestions: vec![],
        },
    }
}

// =============================================================================
// Contract Chat History Endpoints
// =============================================================================

/// Get contract chat history
#[utoipa::path(
    get,
    path = "/api/v1/contracts/{id}/chat/history",
    responses(
        (status = 200, description = "Chat history retrieved successfully", body = ContractChatHistoryResponse),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Contract not found"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = Uuid, Path, description = "Contract ID")
    ),
    security(
        ("bearer_auth" = []),
        ("api_key" = [])
    ),
    tag = "Contracts"
)]
pub async fn get_contract_chat_history(
    State(state): State<SharedState>,
    Authenticated(auth): Authenticated,
    Path(contract_id): Path<Uuid>,
) -> impl IntoResponse {
    let Some(ref pool) = state.db_pool else {
        return (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(json!({ "error": "Database not configured" })),
        )
            .into_response();
    };

    // Verify contract exists and belongs to owner
    match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
        Ok(Some(_)) => {}
        Ok(None) => {
            return (
                StatusCode::NOT_FOUND,
                Json(json!({ "error": "Contract not found" })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Database error: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Database error: {}", e) })),
            )
                .into_response();
        }
    }

    // Get or create conversation
    let conversation = match repository::get_or_create_contract_conversation(pool, contract_id, auth.owner_id).await {
        Ok(conv) => conv,
        Err(e) => {
            tracing::error!("Failed to get contract conversation: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Failed to get conversation: {}", e) })),
            )
                .into_response();
        }
    };

    // Get messages
    let messages = match repository::list_contract_chat_messages(pool, conversation.id, Some(100)).await {
        Ok(msgs) => msgs,
        Err(e) => {
            tracing::error!("Failed to list contract chat messages: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Failed to list messages: {}", e) })),
            )
                .into_response();
        }
    };

    (
        StatusCode::OK,
        Json(ContractChatHistoryResponse {
            contract_id,
            conversation_id: conversation.id,
            messages,
        }),
    )
        .into_response()
}

/// Clear contract chat history (creates a new conversation)
#[utoipa::path(
    delete,
    path = "/api/v1/contracts/{id}/chat/history",
    responses(
        (status = 200, description = "Chat history cleared successfully"),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Contract not found"),
        (status = 500, description = "Internal server error")
    ),
    params(
        ("id" = Uuid, Path, description = "Contract ID")
    ),
    security(
        ("bearer_auth" = []),
        ("api_key" = [])
    ),
    tag = "Contracts"
)]
pub async fn clear_contract_chat_history(
    State(state): State<SharedState>,
    Authenticated(auth): Authenticated,
    Path(contract_id): Path<Uuid>,
) -> impl IntoResponse {
    let Some(ref pool) = state.db_pool else {
        return (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(json!({ "error": "Database not configured" })),
        )
            .into_response();
    };

    // Verify contract exists and belongs to owner
    match repository::get_contract_for_owner(pool, contract_id, auth.owner_id).await {
        Ok(Some(_)) => {}
        Ok(None) => {
            return (
                StatusCode::NOT_FOUND,
                Json(json!({ "error": "Contract not found" })),
            )
                .into_response();
        }
        Err(e) => {
            tracing::error!("Database error: {}", e);
            return (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Database error: {}", e) })),
            )
                .into_response();
        }
    }

    // Clear conversation (archives existing and creates new)
    match repository::clear_contract_conversation(pool, contract_id, auth.owner_id).await {
        Ok(new_conversation) => {
            (
                StatusCode::OK,
                Json(json!({
                    "message": "Chat history cleared",
                    "newConversationId": new_conversation.id
                })),
            )
                .into_response()
        }
        Err(e) => {
            tracing::error!("Failed to clear contract conversation: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(json!({ "error": format!("Failed to clear history: {}", e) })),
            )
                .into_response()
        }
    }
}
