use std::path::Path;
use std::process::Command;

/// Verification status for an AppImage.
#[derive(Debug, Clone)]
pub enum VerificationStatus {
    /// Has a valid embedded GPG signature.
    SignedValid { signer: String },
    /// Has an embedded signature that could not be verified.
    SignedInvalid { reason: String },
    /// No embedded signature found.
    Unsigned,
    /// SHA256 checksum matches the expected value.
    ChecksumMatch,
    /// SHA256 checksum does not match.
    ChecksumMismatch { expected: String, actual: String },
    /// Not checked yet.
    NotChecked,
}

impl VerificationStatus {
    pub fn label(&self) -> String {
        match self {
            Self::SignedValid { signer } => format!("Signed: {}", signer),
            Self::SignedInvalid { reason } => format!("Invalid signature: {}", reason),
            Self::Unsigned => "Unsigned".to_string(),
            Self::ChecksumMatch => "Checksum OK".to_string(),
            Self::ChecksumMismatch { expected, actual } => {
                format!("Mismatch: expected {}... got {}...",
                    &expected[..expected.len().min(12)],
                    &actual[..actual.len().min(12)])
            }
            Self::NotChecked => "Not checked".to_string(),
        }
    }

    pub fn badge_class(&self) -> &'static str {
        match self {
            Self::SignedValid { .. } | Self::ChecksumMatch => "success",
            Self::SignedInvalid { .. } | Self::ChecksumMismatch { .. } => "error",
            Self::Unsigned | Self::NotChecked => "neutral",
        }
    }

    pub fn as_str(&self) -> &'static str {
        match self {
            Self::SignedValid { .. } => "signed_valid",
            Self::SignedInvalid { .. } => "signed_invalid",
            Self::Unsigned => "unsigned",
            Self::ChecksumMatch => "checksum_match",
            Self::ChecksumMismatch { .. } => "checksum_mismatch",
            Self::NotChecked => "not_checked",
        }
    }
}

/// Check for an embedded GPG signature in the AppImage.
/// Uses the `--appimage-signature` flag (Type 2 only).
pub fn check_embedded_signature(path: &Path) -> VerificationStatus {
    // Try to validate with gpg if --appimage-signature outputs something
    let output = Command::new(path)
        .arg("--appimage-signature")
        .env("APPIMAGE_EXTRACT_AND_RUN", "1")
        .output();

    match output {
        Ok(out) if out.status.success() => {
            let stdout = String::from_utf8_lossy(&out.stdout);
            let trimmed = stdout.trim();
            if trimmed.is_empty() {
                return VerificationStatus::Unsigned;
            }

            // Try to verify with gpg
            let gpg_result = Command::new("gpg")
                .arg("--verify")
                .stdin(std::process::Stdio::piped())
                .stdout(std::process::Stdio::piped())
                .stderr(std::process::Stdio::piped())
                .output();

            match gpg_result {
                Ok(gpg_out) => {
                    let stderr = String::from_utf8_lossy(&gpg_out.stderr);
                    if gpg_out.status.success() {
                        // Extract signer info from gpg output
                        let signer = stderr
                            .lines()
                            .find(|l| l.contains("Good signature"))
                            .map(|l| l.trim().to_string())
                            .unwrap_or_else(|| "Valid signature".to_string());
                        VerificationStatus::SignedValid { signer }
                    } else {
                        VerificationStatus::SignedInvalid {
                            reason: stderr.lines().next().unwrap_or("Unknown error").to_string(),
                        }
                    }
                }
                Err(_) => {
                    // gpg not available but signature data exists
                    VerificationStatus::SignedInvalid {
                        reason: "gpg not available for verification".to_string(),
                    }
                }
            }
        }
        _ => VerificationStatus::Unsigned,
    }
}

/// Compute the SHA256 hash of a file.
pub fn compute_sha256(path: &Path) -> Result<String, String> {
    use sha2::{Sha256, Digest};
    use std::io::Read;

    let mut file = std::fs::File::open(path)
        .map_err(|e| format!("Failed to open file: {}", e))?;
    let mut hasher = Sha256::new();
    let mut buf = vec![0u8; 64 * 1024];
    loop {
        let n = file.read(&mut buf)
            .map_err(|e| format!("Read error: {}", e))?;
        if n == 0 {
            break;
        }
        hasher.update(&buf[..n]);
    }
    let hash = hasher.finalize();
    Ok(format!("{:x}", hash))
}

/// Verify a file against an expected SHA256 hash.
pub fn verify_sha256(path: &Path, expected: &str) -> VerificationStatus {
    match compute_sha256(path) {
        Ok(hash) if hash == expected.to_lowercase() => VerificationStatus::ChecksumMatch,
        Ok(hash) => VerificationStatus::ChecksumMismatch {
            expected: expected.to_lowercase(),
            actual: hash,
        },
        Err(_) => VerificationStatus::NotChecked,
    }
}

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

    #[test]
    fn test_verification_status_labels() {
        assert_eq!(VerificationStatus::Unsigned.label(), "Unsigned");
        assert_eq!(VerificationStatus::NotChecked.label(), "Not checked");
        assert_eq!(VerificationStatus::ChecksumMatch.label(), "Checksum OK");
        assert!(VerificationStatus::SignedValid { signer: "test".into() }
            .label().contains("test"));
        assert!(VerificationStatus::ChecksumMismatch {
            expected: "abc123456789".into(),
            actual: "def987654321".into(),
        }.label().contains("Mismatch"));
    }

    #[test]
    fn test_verification_status_badge_classes() {
        assert_eq!(VerificationStatus::ChecksumMatch.badge_class(), "success");
        assert_eq!(VerificationStatus::Unsigned.badge_class(), "neutral");
    }
}
