//! Code Review MCP Server implementation

use std::path::PathBuf;
use std::sync::Arc;

use crate::{
    IterationStatus, ReviewCategory, ReviewFinding, ReviewSession, ReviewStatus, ReviewSummary,
    Severity, StateManager,
};
use rmcp::{
    model::{CallToolResult, Content, ServerCapabilities, ServerInfo},
    schemars, tool, Error as McpError, ServerHandler,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Default maximum iterations before asking human
const DEFAULT_MAX_ITERATIONS: u32 = 3;

/// Code Review MCP Server
#[derive(Clone)]
pub struct CodeReviewServer {
    state: Arc<StateManager>,
    max_iterations: u32,
}

// ==================== Request Types ====================

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CreateReviewRequest {
    #[schemars(description = "Name of the task being reviewed")]
    pub task_name: String,
    #[schemars(description = "List of file paths to review")]
    pub files: Vec<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ReviewIdRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct TaskNameRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SubmitFindingsRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(
        description = "JSON array of findings with category, file, severity, description, and optional line/suggestion"
    )]
    pub findings_json: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MarkResolvedRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(description = "ID of the finding to mark as resolved")]
    pub finding_id: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CompleteIterationRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(description = "Whether to start the next iteration if needed")]
    pub start_next: bool,
}

// ==================== Internal Types ====================

#[derive(Debug, Serialize, Deserialize)]
pub struct FindingInput {
    pub category: String,
    pub file: String,
    #[serde(default)]
    pub line: Option<u32>,
    pub severity: String,
    pub description: String,
    #[serde(default)]
    pub suggestion: Option<String>,
}

// ==================== Server Implementation ====================

impl CodeReviewServer {
    pub fn new(data_dir: PathBuf) -> Self {
        Self {
            state: Arc::new(StateManager::new(data_dir)),
            max_iterations: DEFAULT_MAX_ITERATIONS,
        }
    }

    pub fn with_max_iterations(mut self, max: u32) -> Self {
        self.max_iterations = max;
        self
    }

    fn parse_category(s: &str) -> ReviewCategory {
        match s.to_lowercase().as_str() {
            "bugs" | "bug" => ReviewCategory::Bugs,
            "best_practices" | "best-practices" | "bestpractices" => ReviewCategory::BestPractices,
            "security" => ReviewCategory::Security,
            "performance" => ReviewCategory::Performance,
            "simplification" | "simplify" => ReviewCategory::Simplification,
            _ => ReviewCategory::Bugs,
        }
    }

    fn parse_severity(s: &str) -> Severity {
        match s.to_lowercase().as_str() {
            "critical" => Severity::Critical,
            "high" => Severity::High,
            "medium" => Severity::Medium,
            "low" => Severity::Low,
            "info" | "informational" => Severity::Info,
            _ => Severity::Medium,
        }
    }
}

#[tool(tool_box)]
impl CodeReviewServer {
    /// Create a new review session for a task
    #[tool(description = "Create a new review session for a task. Returns the review session ID.")]
    async fn create_review_session(
        &self,
        #[tool(aggr)] req: CreateReviewRequest,
    ) -> Result<CallToolResult, McpError> {
        // Determine iteration number
        let reviews = self
            .state
            .list_reviews(&req.task_name)
            .await
            .unwrap_or_default();
        let iteration = reviews.len() as u32 + 1;

        // Create new session
        let review_id = Uuid::new_v4().to_string();
        let mut session = ReviewSession::new(&review_id, &req.task_name, iteration);
        session.files_reviewed = req.files.clone();
        session.status = ReviewStatus::InProgress;

        // Save session
        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let response = serde_json::json!({
            "success": true,
            "review_id": review_id,
            "task_name": req.task_name,
            "iteration": iteration,
            "files_to_review": req.files,
            "instructions": "Review the files from 5 perspectives: bugs, best_practices, security, performance, simplification. Submit findings using submit_findings tool."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    /// Get the current state of a review session
    #[tool(description = "Get the current state and findings of a review session")]
    async fn get_review_session(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let json = serde_json::to_string_pretty(&session)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    /// List all reviews for a task
    #[tool(description = "List all review sessions for a task with summary information")]
    async fn list_reviews(
        &self,
        #[tool(aggr)] req: TaskNameRequest,
    ) -> Result<CallToolResult, McpError> {
        let reviews = self
            .state
            .list_reviews(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let mut summaries: Vec<ReviewSummary> = Vec::new();
        for review_id in reviews {
            if let Ok(session) = self.state.load_review(&req.task_name, &review_id).await {
                summaries.push(ReviewSummary::from(&session));
            }
        }

        let json = serde_json::to_string_pretty(&summaries)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    /// Submit findings from code review
    #[tool(
        description = "Submit findings from code review. Findings should include category (bugs, best_practices, security, performance, simplification), file, severity (critical, high, medium, low, info), and description."
    )]
    async fn submit_findings(
        &self,
        #[tool(aggr)] req: SubmitFindingsRequest,
    ) -> Result<CallToolResult, McpError> {
        // Parse findings
        let finding_inputs: Vec<FindingInput> = serde_json::from_str(&req.findings_json)
            .map_err(|e| McpError::invalid_params(format!("Invalid findings JSON: {}", e), None))?;

        // Load session
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Convert to ReviewFinding structs
        let new_findings: Vec<ReviewFinding> = finding_inputs
            .into_iter()
            .map(|f| {
                let category = Self::parse_category(&f.category);
                let severity = Self::parse_severity(&f.severity);
                let id = Uuid::new_v4().to_string();

                let mut finding =
                    ReviewFinding::new(&id, category, &f.file, severity, &f.description);
                if let Some(line) = f.line {
                    finding = finding.with_line(line);
                }
                if let Some(ref suggestion) = f.suggestion {
                    finding = finding.with_suggestion(suggestion);
                }
                finding
            })
            .collect();

        let count = new_findings.len();
        session.findings.extend(new_findings);

        // Save session
        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Generate summary
        let summary = ReviewSummary::from(&session);

        let response = serde_json::json!({
            "success": true,
            "findings_added": count,
            "total_findings": summary.total_findings,
            "critical_findings": summary.critical_findings,
            "high_findings": summary.high_findings,
            "message": format!("{} findings added to review session.", count)
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    /// Mark a finding as resolved
    #[tool(description = "Mark a specific finding as resolved after it has been addressed")]
    async fn mark_finding_resolved(
        &self,
        #[tool(aggr)] req: MarkResolvedRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Find and resolve the finding
        let mut found = false;
        for finding in &mut session.findings {
            if finding.id == req.finding_id {
                finding.resolved = true;
                found = true;
                break;
            }
        }

        if !found {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Finding '{}' not found\"}}",
                req.finding_id
            ))]));
        }

        // Save session
        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let summary = ReviewSummary::from(&session);

        let response = serde_json::json!({
            "success": true,
            "unresolved_remaining": summary.unresolved_findings,
            "message": "Finding marked as resolved."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    /// Check if the iteration limit has been reached
    #[tool(
        description = "Check if the review iteration limit has been reached and whether human intervention is needed"
    )]
    async fn check_iteration_limit(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let status = IterationStatus::new(session.iteration, self.max_iterations);

        let json = serde_json::to_string_pretty(&status)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    /// Approve the review session
    #[tool(description = "Approve the review session, marking it as fully reviewed and accepted")]
    async fn approve_review(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Check for unresolved critical/high findings
        let critical = session.critical_findings().len();
        let high = session.high_findings().len();

        if critical > 0 || high > 0 {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Cannot approve: {} critical and {} high severity findings remain unresolved.\"}}",
                critical, high
            ))]));
        }

        session.approve();

        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let response = serde_json::json!({
            "success": true,
            "status": "approved",
            "message": "Review session approved. Code is ready for deployment."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    /// Complete the current iteration and optionally start the next
    #[tool(
        description = "Complete the current review iteration. If unresolved findings remain, optionally start the next iteration."
    )]
    async fn complete_iteration(
        &self,
        #[tool(aggr)] req: CompleteIterationRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let summary = ReviewSummary::from(&session);

        // Complete current session
        session.complete();
        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Check if we need another iteration
        if summary.unresolved_findings == 0 {
            let response = serde_json::json!({
                "success": true,
                "status": "complete",
                "all_resolved": true,
                "message": "All findings resolved. Consider approving the review."
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        // Check iteration limit
        let iteration_status = IterationStatus::new(session.iteration, self.max_iterations);

        if iteration_status.needs_human_intervention && !req.start_next {
            let response = serde_json::json!({
                "success": true,
                "status": "iteration_limit_reached",
                "unresolved_findings": summary.unresolved_findings,
                "iteration": session.iteration,
                "max_iterations": self.max_iterations,
                "message": iteration_status.message
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        if req.start_next {
            // Create next iteration
            let new_id = Uuid::new_v4().to_string();
            let mut new_session =
                ReviewSession::new(&new_id, &req.task_name, session.iteration + 1);
            new_session.files_reviewed = session.files_reviewed.clone();
            new_session.status = ReviewStatus::InProgress;

            self.state
                .save_review(&req.task_name, &new_session)
                .await
                .map_err(|e| McpError::internal_error(e.to_string(), None))?;

            let response = serde_json::json!({
                "success": true,
                "status": "next_iteration_started",
                "new_review_id": new_id,
                "iteration": session.iteration + 1,
                "unresolved_from_previous": summary.unresolved_findings,
                "message": format!("Iteration {} started. Review the fixes and check for remaining issues.", session.iteration + 1)
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        let response = serde_json::json!({
            "success": true,
            "status": "complete_with_unresolved",
            "unresolved_findings": summary.unresolved_findings,
            "message": "Iteration complete but unresolved findings remain. Use start_next=true to begin next iteration."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    /// Get summary of unresolved findings by category and severity
    #[tool(description = "Get a summary breakdown of unresolved findings by category and severity")]
    async fn get_findings_summary(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let unresolved = session.unresolved_findings();

        // Count by category
        let mut by_category: std::collections::HashMap<String, usize> =
            std::collections::HashMap::new();
        let mut by_severity: std::collections::HashMap<String, usize> =
            std::collections::HashMap::new();

        for finding in &unresolved {
            *by_category.entry(finding.category.to_string()).or_insert(0) += 1;
            *by_severity.entry(finding.severity.to_string()).or_insert(0) += 1;
        }

        let response = serde_json::json!({
            "review_id": req.review_id,
            "total_unresolved": unresolved.len(),
            "by_category": by_category,
            "by_severity": by_severity,
            "findings": unresolved.iter().map(|f| serde_json::json!({
                "id": f.id,
                "category": f.category.to_string(),
                "severity": f.severity.to_string(),
                "file": f.file,
                "line": f.line,
                "description": f.description
            })).collect::<Vec<_>>()
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }
}

#[tool(tool_box)]
impl ServerHandler for CodeReviewServer {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            protocol_version: Default::default(),
            capabilities: ServerCapabilities::builder().enable_tools().build(),
            server_info: rmcp::model::Implementation::from_build_env(),
            instructions: Some(
                "Code Review MCP Server for Koder 4000. Reviews code from 5 perspectives: bugs, best practices, security, performance, and simplification. Manages review iterations and findings.".to_string(),
            ),
        }
    }
}
