use std::path::PathBuf;

use clap::Parser;
use serde_json::json;

use crate::completion::GapAnalyzer;
/// CLI command implementations
use crate::{Completer, StackDetector};

/// Detect command - analyze project and infer requirements
#[derive(Parser, Debug)]
pub struct DetectCommand {
    /// Path to project directory
    #[arg(value_name = "PATH")]
    pub path: PathBuf,

    /// Output format (json, yaml, or text)
    #[arg(short, long, default_value = "text")]
    pub format: OutputFormat,

    /// Show only high-confidence detections
    #[arg(short = 'C', long)]
    pub high_confidence_only: bool,

    /// Pretty-print JSON output
    #[arg(short = 'p', long)]
    pub pretty: bool,
}

#[derive(Debug, Clone, Copy)]
pub enum OutputFormat {
    Json,
    Yaml,
    Text,
}

impl std::str::FromStr for OutputFormat {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "json" => Ok(OutputFormat::Json),
            "yaml" | "yml" => Ok(OutputFormat::Yaml),
            "text" => Ok(OutputFormat::Text),
            _ => Err(format!("Invalid format: {}", s)),
        }
    }
}

impl std::fmt::Display for OutputFormat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            OutputFormat::Json => write!(f, "json"),
            OutputFormat::Yaml => write!(f, "yaml"),
            OutputFormat::Text => write!(f, "text"),
        }
    }
}

impl DetectCommand {
    pub fn execute(&self) -> anyhow::Result<()> {
        // Verify path exists
        if !self.path.exists() {
            anyhow::bail!("Project path does not exist: {}", self.path.display());
        }

        // Run detection
        let detector = StackDetector::new();
        let analysis = detector.detect_all(&self.path)?;

        // Filter by confidence if requested
        let detections = if self.high_confidence_only {
            analysis
                .detections
                .iter()
                .filter(|d| d.is_high_confidence())
                .cloned()
                .collect::<Vec<_>>()
        } else {
            analysis.detections.clone()
        };

        // Output results
        match self.format {
            OutputFormat::Json => self.output_json(&analysis, &detections)?,
            OutputFormat::Yaml => self.output_yaml(&analysis, &detections)?,
            OutputFormat::Text => self.output_text(&analysis, &detections)?,
        }

        Ok(())
    }

    fn output_json(
        &self,
        analysis: &crate::ProjectAnalysis,
        detections: &[crate::Detection],
    ) -> anyhow::Result<()> {
        let output = json!({
            "project_path": analysis.project_path.display().to_string(),
            "overall_confidence": analysis.overall_confidence,
            "detections": detections.iter().map(|d| json!({
                "technology": d.technology.as_str(),
                "confidence": d.confidence,
                "evidence_count": d.evidence.len(),
            })).collect::<Vec<_>>(),
            "requirements": analysis.requirements.iter().map(|r| json!({
                "taskserv": r.taskserv,
                "reason": r.reason,
                "confidence": r.confidence,
                "required": r.required,
            })).collect::<Vec<_>>(),
        });

        if self.pretty {
            println!("{}", serde_json::to_string_pretty(&output)?);
        } else {
            println!("{}", output);
        }

        Ok(())
    }

    fn output_yaml(
        &self,
        analysis: &crate::ProjectAnalysis,
        detections: &[crate::Detection],
    ) -> anyhow::Result<()> {
        // Convert JSON to YAML for simplicity
        let json_output = json!({
            "project_path": analysis.project_path.display().to_string(),
            "overall_confidence": analysis.overall_confidence,
            "detections": detections.iter().map(|d| json!({
                "technology": d.technology.as_str(),
                "confidence": d.confidence,
                "evidence_count": d.evidence.len(),
            })).collect::<Vec<_>>(),
            "requirements": analysis.requirements.iter().map(|r| json!({
                "taskserv": r.taskserv,
                "reason": r.reason,
                "confidence": r.confidence,
                "required": r.required,
            })).collect::<Vec<_>>(),
        });

        // For now, use JSON with pretty printing as YAML equivalent
        println!("{}", serde_json::to_string_pretty(&json_output)?);
        Ok(())
    }

    fn output_text(
        &self,
        analysis: &crate::ProjectAnalysis,
        detections: &[crate::Detection],
    ) -> anyhow::Result<()> {
        println!("\n╔════════════════════════════════════════════════╗");
        println!("║          Project Technology Detection          ║");
        println!("╚════════════════════════════════════════════════╝\n");

        println!("Project Path: {}", analysis.project_path.display());
        println!(
            "Overall Confidence: {:.1}%\n",
            analysis.overall_confidence * 100.0
        );

        if !detections.is_empty() {
            println!("Detected Technologies:");
            for detection in detections {
                let confidence_indicator = if detection.is_high_confidence() {
                    "✓"
                } else {
                    "○"
                };
                println!(
                    "  {} {:<15} - {:.1}% confidence ({} evidence)",
                    confidence_indicator,
                    detection.technology.as_str(),
                    detection.confidence * 100.0,
                    detection.evidence.len()
                );
            }
            println!();
        }

        if !analysis.requirements.is_empty() {
            println!("Inferred Requirements:");
            let required: Vec<_> = analysis
                .requirements
                .iter()
                .filter(|r| r.required)
                .collect();
            let optional: Vec<_> = analysis
                .requirements
                .iter()
                .filter(|r| !r.required)
                .collect();

            if !required.is_empty() {
                println!("  Required:");
                for req in required {
                    println!(
                        "    • {:<20} - {:.1}% confidence ({})",
                        req.taskserv,
                        req.confidence * 100.0,
                        req.reason
                    );
                }
            }

            if !optional.is_empty() {
                println!("  Optional:");
                for req in optional {
                    println!(
                        "    • {:<20} - {:.1}% confidence ({})",
                        req.taskserv,
                        req.confidence * 100.0,
                        req.reason
                    );
                }
            }
            println!();
        }

        Ok(())
    }
}

/// Complete command - analyze gaps and suggest completions
#[derive(Parser, Debug)]
pub struct CompleteCommand {
    /// Path to project directory
    #[arg(value_name = "PATH")]
    pub path: PathBuf,

    /// Output format (json, yaml, or text)
    #[arg(short, long, default_value = "text")]
    pub format: OutputFormat,

    /// Check mode (don't apply changes)
    #[arg(short = 'c', long)]
    pub check: bool,

    /// Pretty-print JSON output
    #[arg(short = 'p', long)]
    pub pretty: bool,
}

impl CompleteCommand {
    pub fn execute(&self) -> anyhow::Result<()> {
        // Verify path exists
        if !self.path.exists() {
            anyhow::bail!("Project path does not exist: {}", self.path.display());
        }

        // Run detection first
        let detector = StackDetector::new();
        let analysis = detector.detect_all(&self.path)?;

        // Analyze gaps
        let required_taskservs: Vec<String> = analysis
            .requirements
            .iter()
            .filter(|r| r.required)
            .map(|r| r.taskserv.clone())
            .collect();

        let gaps = GapAnalyzer::analyze(&analysis, &required_taskservs);

        // Plan completion
        let completer = Completer::new();
        let completion_result = completer.complete(&analysis, Vec::new());

        // Output results
        match self.format {
            OutputFormat::Json => self.output_json(&gaps, &completion_result)?,
            OutputFormat::Yaml => self.output_yaml(&gaps, &completion_result)?,
            OutputFormat::Text => self.output_text(&gaps, &completion_result)?,
        }

        Ok(())
    }

    fn output_json(
        &self,
        gaps: &crate::completion::GapAnalysisResult,
        result: &crate::completion::CompletionResult,
    ) -> anyhow::Result<()> {
        let output = json!({
            "completeness": gaps.completeness,
            "gaps": gaps.gaps.iter().map(|gap| json!({
                "severity": format!("{:?}", gap.severity),
                "message": gap.message,
                "suggestion": gap.suggestion,
            })).collect::<Vec<_>>(),
            "error_count": gaps.error_count,
            "warning_count": gaps.warning_count,
            "changes_needed": result.changes_needed,
            "is_safe": result.is_safe,
            "version_bump": result.version_bump,
            "change_summary": result.change_summary,
        });

        if self.pretty {
            println!("{}", serde_json::to_string_pretty(&output)?);
        } else {
            println!("{}", output);
        }

        Ok(())
    }

    fn output_yaml(
        &self,
        gaps: &crate::completion::GapAnalysisResult,
        result: &crate::completion::CompletionResult,
    ) -> anyhow::Result<()> {
        let json_output = json!({
            "completeness": gaps.completeness,
            "gaps": gaps.gaps.iter().map(|gap| json!({
                "severity": format!("{:?}", gap.severity),
                "message": gap.message,
                "suggestion": gap.suggestion,
            })).collect::<Vec<_>>(),
            "error_count": gaps.error_count,
            "warning_count": gaps.warning_count,
            "changes_needed": result.changes_needed,
            "is_safe": result.is_safe,
            "version_bump": result.version_bump,
            "change_summary": result.change_summary,
        });

        println!("{}", serde_json::to_string_pretty(&json_output)?);
        Ok(())
    }

    fn output_text(
        &self,
        gaps: &crate::completion::GapAnalysisResult,
        result: &crate::completion::CompletionResult,
    ) -> anyhow::Result<()> {
        println!("\n╔════════════════════════════════════════════════╗");
        println!("║      Infrastructure Declaration Completion     ║");
        println!("╚════════════════════════════════════════════════╝\n");

        println!("Completeness: {:.1}%", gaps.completeness * 100.0);
        println!(
            "Errors: {} | Warnings: {} | Info: {}",
            gaps.error_count, gaps.warning_count, gaps.info_count
        );
        println!();

        if !gaps.gaps.is_empty() {
            println!("Identified Gaps:");
            for gap in &gaps.gaps {
                let severity_icon = match gap.severity {
                    crate::completion::Severity::Error => "✗",
                    crate::completion::Severity::Warning => "⚠",
                    crate::completion::Severity::Info => "ℹ",
                };
                println!("  {} {}", severity_icon, gap.message);
                println!("    → {}", gap.suggestion);
            }
            println!();
        }

        println!("Completion Plan:");
        println!("  Changes Needed: {}", result.changes_needed);
        println!(
            "  Is Safe: {}",
            if result.is_safe { "✓ Yes" } else { "✗ No" }
        );
        println!("  Version Bump: {}", result.version_bump);
        println!("  Summary: {}", result.change_summary);
        println!();

        if !self.check {
            println!("Mode: Apply changes");
        } else {
            println!("Mode: Check only (no changes applied)");
        }
        println!();

        Ok(())
    }
}

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

    #[test]
    fn test_output_format_from_str() {
        assert!(matches!(
            "json".parse::<OutputFormat>(),
            Ok(OutputFormat::Json)
        ));
        assert!(matches!(
            "yaml".parse::<OutputFormat>(),
            Ok(OutputFormat::Yaml)
        ));
        assert!(matches!(
            "text".parse::<OutputFormat>(),
            Ok(OutputFormat::Text)
        ));
    }

    #[test]
    fn test_output_format_display() {
        assert_eq!(OutputFormat::Json.to_string(), "json");
        assert_eq!(OutputFormat::Yaml.to_string(), "yaml");
        assert_eq!(OutputFormat::Text.to_string(), "text");
    }

    #[test]
    fn test_detect_command_creation() {
        let cmd = DetectCommand {
            path: PathBuf::from("/tmp"),
            format: OutputFormat::Text,
            high_confidence_only: false,
            pretty: false,
        };
        assert_eq!(cmd.path, PathBuf::from("/tmp"));
    }
}
