use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use uuid::Uuid;

/// Severity level for vulnerabilities
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
    Low,
    Medium,
    High,
    Critical,
}

impl std::fmt::Display for Severity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Severity::Low => write!(f, "low"),
            Severity::Medium => write!(f, "medium"),
            Severity::High => write!(f, "high"),
            Severity::Critical => write!(f, "critical"),
        }
    }
}

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

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "low" => Ok(Severity::Low),
            "medium" => Ok(Severity::Medium),
            "high" => Ok(Severity::High),
            "critical" => Ok(Severity::Critical),
            _ => Err(format!("Unknown severity: {}", s)),
        }
    }
}

/// Type of quantum vulnerability
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum VulnerabilityType {
    Rsa1024,
    Rsa2048,
    Rsa4096,
    RsaOther,
    Ecdsa,
    Ecdh,
    EcdsaP256,
    EcdsaP384,
    EcdsaP521,
    Dsa,
    Dh,
    DhKeyExchange,
    Sha1,
    Md5,
    TripleDes,
    Des,
    Rc4,
    Blowfish,
    WeakRandom,
    HardcodedKey,
    InsecureTls,
    /// Library that is purely quantum-vulnerable (e.g., 'rsa', 'ecdsa')
    VulnerableLibrary,
    /// Multi-purpose library that contains quantum-vulnerable algorithms
    VulnerableLibraryMixed,
}

impl std::fmt::Display for VulnerabilityType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VulnerabilityType::Rsa1024 => write!(f, "RSA-1024"),
            VulnerabilityType::Rsa2048 => write!(f, "RSA-2048"),
            VulnerabilityType::Rsa4096 => write!(f, "RSA-4096"),
            VulnerabilityType::RsaOther => write!(f, "RSA"),
            VulnerabilityType::Ecdsa => write!(f, "ECDSA"),
            VulnerabilityType::Ecdh => write!(f, "ECDH"),
            VulnerabilityType::EcdsaP256 => write!(f, "ECDSA-P256"),
            VulnerabilityType::EcdsaP384 => write!(f, "ECDSA-P384"),
            VulnerabilityType::EcdsaP521 => write!(f, "ECDSA-P521"),
            VulnerabilityType::Dsa => write!(f, "DSA"),
            VulnerabilityType::Dh => write!(f, "DH"),
            VulnerabilityType::DhKeyExchange => write!(f, "DH-Key-Exchange"),
            VulnerabilityType::Sha1 => write!(f, "SHA-1"),
            VulnerabilityType::Md5 => write!(f, "MD5"),
            VulnerabilityType::TripleDes => write!(f, "3DES"),
            VulnerabilityType::Des => write!(f, "DES"),
            VulnerabilityType::Rc4 => write!(f, "RC4"),
            VulnerabilityType::Blowfish => write!(f, "Blowfish"),
            VulnerabilityType::WeakRandom => write!(f, "Weak-Random"),
            VulnerabilityType::HardcodedKey => write!(f, "Hardcoded-Key"),
            VulnerabilityType::InsecureTls => write!(f, "Insecure-TLS"),
            VulnerabilityType::VulnerableLibrary => write!(f, "Vulnerable-Library"),
            VulnerabilityType::VulnerableLibraryMixed => write!(f, "Library-Audit"),
        }
    }
}

/// Location information for a vulnerability
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Location {
    pub file: PathBuf,
    pub line: usize,
    pub column: usize,
    pub end_line: Option<usize>,
    pub end_column: Option<usize>,
}

/// Migration impact type
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ImpactType {
    /// New PQC key won't fit in existing database column
    DatabaseSchema,
    /// Larger keys may exceed API payload limits
    ApiPayload,
    /// Requires architectural refactoring (e.g., RSA to KEM)
    ArchitecturalRefactor,
    /// Certificate/PKI changes required
    CertificateInfrastructure,
    /// Performance degradation expected
    Performance,
    /// Protocol changes required
    ProtocolChange,
}

impl std::fmt::Display for ImpactType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ImpactType::DatabaseSchema => write!(f, "Database Schema"),
            ImpactType::ApiPayload => write!(f, "API Payload"),
            ImpactType::ArchitecturalRefactor => write!(f, "Architectural Refactor"),
            ImpactType::CertificateInfrastructure => write!(f, "Certificate Infrastructure"),
            ImpactType::Performance => write!(f, "Performance"),
            ImpactType::ProtocolChange => write!(f, "Protocol Change"),
        }
    }
}

/// Migration impact details
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MigrationImpact {
    pub impact_type: ImpactType,
    pub description: String,
    pub affected_components: Vec<String>,
    pub estimated_effort: String,
    pub recommendation: String,
}

/// Type of finding (direct usage vs import warning)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum FindingType {
    /// Direct usage of a vulnerable algorithm (e.g., RSA.generate())
    #[default]
    DirectUsage,
    /// Import of a purely vulnerable library (e.g., import rsa)
    VulnerableImport,
    /// Import of a mixed-purpose library that may contain vulnerabilities
    AuditRequired,
}

impl std::fmt::Display for FindingType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FindingType::DirectUsage => write!(f, "Direct Usage"),
            FindingType::VulnerableImport => write!(f, "Vulnerable Import"),
            FindingType::AuditRequired => write!(f, "Audit Required"),
        }
    }
}

/// A single vulnerability finding
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Vulnerability {
    pub id: Uuid,
    pub vuln_type: VulnerabilityType,
    pub severity: Severity,
    pub location: Location,
    pub description: String,
    pub code_snippet: Option<String>,
    pub recommendation: String,
    pub migration_impacts: Vec<MigrationImpact>,
    pub cwe_id: Option<String>,
    pub is_dependency: bool,
    pub dependency_chain: Option<Vec<String>>,
    /// Type of finding: direct usage, vulnerable import, or audit required
    #[serde(default)]
    pub finding_type: FindingType,
    /// Name of the library if this is an import finding
    #[serde(skip_serializing_if = "Option::is_none")]
    pub library_name: Option<String>,
}

/// Programming language
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Language {
    JavaScript,
    TypeScript,
    Python,
    Go,
    Java,
    Rust,
    C,
    Cpp,
    CSharp,
    Php,
    Ruby,
    Kotlin,
    Swift,
    Scala,
    Shell,
    Perl,
    Lua,
    ObjectiveC,
    Dart,
    Groovy,
    Haskell,
    Elixir,
    R,
    FSharp,
    Clojure,
    Solidity,
    Unknown,
}

impl Language {
    pub fn from_extension(ext: &str) -> Self {
        match ext.to_lowercase().as_str() {
            // JavaScript/TypeScript
            "js" | "jsx" | "mjs" | "cjs" => Language::JavaScript,
            "ts" | "tsx" | "mts" | "cts" => Language::TypeScript,
            // Python
            "py" | "pyw" | "pyi" => Language::Python,
            // Go
            "go" => Language::Go,
            // Java/JVM
            "java" => Language::Java,
            "kt" | "kts" => Language::Kotlin,
            "scala" | "sc" => Language::Scala,
            "groovy" | "gvy" | "gy" | "gsh" => Language::Groovy,
            // Rust
            "rs" => Language::Rust,
            // C/C++
            "c" | "h" => Language::C,
            "cpp" | "cc" | "cxx" | "hpp" | "hxx" | "c++" | "h++" => Language::Cpp,
            // C#
            "cs" | "csx" => Language::CSharp,
            // PHP
            "php" | "phtml" | "php3" | "php4" | "php5" | "php7" | "phps" => Language::Php,
            // Ruby
            "rb" | "rbw" | "rake" | "gemspec" => Language::Ruby,
            // Swift
            "swift" => Language::Swift,
            // Objective-C
            "m" | "mm" => Language::ObjectiveC,
            // Shell/Bash
            "sh" | "bash" | "zsh" | "ksh" | "fish" => Language::Shell,
            // Perl
            "pl" | "pm" | "perl" => Language::Perl,
            // Lua
            "lua" => Language::Lua,
            // Dart
            "dart" => Language::Dart,
            // Haskell
            "hs" | "lhs" => Language::Haskell,
            // Elixir
            "ex" | "exs" => Language::Elixir,
            // R
            "r" | "R" | "rmd" | "Rmd" => Language::R,
            // F#
            "fs" | "fsx" | "fsi" => Language::FSharp,
            // Clojure
            "clj" | "cljs" | "cljc" | "edn" => Language::Clojure,
            // Solidity
            "sol" => Language::Solidity,
            _ => Language::Unknown,
        }
    }
}

/// File scan result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileResult {
    pub path: PathBuf,
    pub language: Language,
    pub vulnerabilities: Vec<Vulnerability>,
    pub lines_scanned: usize,
    pub scan_duration_ms: u64,
}

/// Complete scan results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanResults {
    #[serde(default)]
    pub vulnerability_count: usize,
    pub scan_id: Uuid,
    pub repo_id: Option<String>,
    pub started_at: DateTime<Utc>,
    pub completed_at: DateTime<Utc>,
    pub scanned_path: PathBuf,
    pub files_scanned: usize,
    pub lines_scanned: usize,
    pub vulnerabilities: Vec<Vulnerability>,
    pub file_results: Vec<FileResult>,
    pub has_db_impact: bool,
    pub has_api_impact: bool,
    pub summary: ScanSummary,
}

/// Summary statistics for a scan
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScanSummary {
    pub total_vulnerabilities: usize,
    pub critical_count: usize,
    pub high_count: usize,
    pub medium_count: usize,
    pub low_count: usize,
    pub by_type: std::collections::HashMap<String, usize>,
    pub by_language: std::collections::HashMap<String, usize>,
    pub dependency_vulnerabilities: usize,
    pub direct_vulnerabilities: usize,
}

impl ScanSummary {
    pub fn from_vulnerabilities(vulns: &[Vulnerability]) -> Self {
        use std::collections::HashMap;

        let mut by_type: HashMap<String, usize> = HashMap::new();
        let mut by_language: HashMap<String, usize> = HashMap::new();
        let mut critical_count = 0;
        let mut high_count = 0;
        let mut medium_count = 0;
        let mut low_count = 0;
        let mut dependency_vulnerabilities = 0;

        for vuln in vulns {
            *by_type.entry(vuln.vuln_type.to_string()).or_insert(0) += 1;

            match vuln.severity {
                Severity::Critical => critical_count += 1,
                Severity::High => high_count += 1,
                Severity::Medium => medium_count += 1,
                Severity::Low => low_count += 1,
            }

            if vuln.is_dependency {
                dependency_vulnerabilities += 1;
            }
        }

        Self {
            total_vulnerabilities: vulns.len(),
            critical_count,
            high_count,
            medium_count,
            low_count,
            by_type,
            by_language,
            dependency_vulnerabilities,
            direct_vulnerabilities: vulns.len() - dependency_vulnerabilities,
        }
    }
}

/// Anonymized payload for API submission (no source code)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubmissionPayload {
    pub scan_id: Uuid,
    pub repo_id: Option<String>,
    pub scanned_at: DateTime<Utc>,
    pub files_scanned: usize,
    pub lines_scanned: usize,
    pub vulnerabilities: Vec<VulnerabilitySummary>,
    pub has_db_impact: bool,
    pub has_api_impact: bool,
    pub summary: ScanSummary,
}

impl SubmissionPayload {
    pub fn from_results(results: &ScanResults) -> Self {
        Self {
            scan_id: results.scan_id,
            repo_id: results.repo_id.clone(),
            scanned_at: results.completed_at,
            files_scanned: results.files_scanned,
            lines_scanned: results.lines_scanned,
            vulnerabilities: results
                .vulnerabilities
                .iter()
                .map(VulnerabilitySummary::from_vulnerability)
                .collect(),
            has_db_impact: results.has_db_impact,
            has_api_impact: results.has_api_impact,
            summary: results.summary.clone(),
        }
    }
}

/// Anonymized vulnerability summary (includes all fields API expects)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VulnerabilitySummary {
    pub id: Uuid,
    #[serde(rename = "vuln_type")]
    pub vuln_type: VulnerabilityType,
    pub severity: Severity,
    pub file: String,
    pub line: usize,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub column: Option<usize>,
    #[serde(rename = "end_line", skip_serializing_if = "Option::is_none")]
    pub end_line: Option<usize>,
    #[serde(rename = "end_column", skip_serializing_if = "Option::is_none")]
    pub end_column: Option<usize>,
    #[serde(rename = "code_snippet", skip_serializing_if = "Option::is_none")]
    pub code_snippet: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub recommendation: Option<String>,
    #[serde(rename = "cwe_id", skip_serializing_if = "Option::is_none")]
    pub cwe_id: Option<String>,
    #[serde(rename = "is_dependency")]
    pub is_dependency: bool,
    #[serde(rename = "dependency_chain", skip_serializing_if = "Option::is_none")]
    pub dependency_chain: Option<Vec<String>>,
    #[serde(rename = "impact_types")]
    pub impact_types: Vec<String>,
}

impl VulnerabilitySummary {
    pub fn from_vulnerability(vuln: &Vulnerability) -> Self {
        Self {
            id: vuln.id,
            vuln_type: vuln.vuln_type.clone(),
            severity: vuln.severity,
            file: vuln.location.file.to_string_lossy().to_string(),
            line: vuln.location.line,
            column: Some(vuln.location.column),
            end_line: vuln.location.end_line,
            end_column: vuln.location.end_column,
            code_snippet: vuln.code_snippet.clone(),
            description: Some(vuln.description.clone()),
            recommendation: Some(vuln.recommendation.clone()),
            cwe_id: vuln.cwe_id.clone(),
            is_dependency: vuln.is_dependency,
            dependency_chain: vuln.dependency_chain.clone(),
            impact_types: vuln.migration_impacts.iter().map(|i| {
                // Convert ImpactType enum to snake_case string
                match i.impact_type {
                    ImpactType::DatabaseSchema => "database_schema".to_string(),
                    ImpactType::ApiPayload => "api_payload".to_string(),
                    ImpactType::ArchitecturalRefactor => "architectural_refactor".to_string(),
                    ImpactType::CertificateInfrastructure => "certificate_infrastructure".to_string(),
                    ImpactType::Performance => "performance".to_string(),
                    ImpactType::ProtocolChange => "protocol_change".to_string(),
                }
            }).collect(),
        }
    }
}
