use crate::declare_detector;
use crate::diagnostic::{Action, Diagnostic, FilesystemAction, WatchAction};
use crate::watch::COMMON_PGPSIGURL_MANGLES;
use crate::{Certainty, FixerError, FixerPreferences, LintianIssue, Visibility};
use debian_watch::{mangle, Release};
use debian_workspace::Workspace;
use sequoia_openpgp as openpgp;
use std::collections::HashSet;
use std::path::{Path, PathBuf};

const NUM_KEYS_TO_CHECK: usize = 5;
const RELEASES_TO_INSPECT: usize = 5;

#[derive(Debug)]
enum VerificationStatus {
    /// No keyring available, signature not verified (discovery mode)
    Unverified,
    /// Signature verified successfully with keyring
    Verified,
    /// Signature verification failed with keyring (wrong key or corrupted signature)
    Failed,
}

#[derive(Debug)]
struct SignatureInfo {
    verification_status: VerificationStatus,
    keys: HashSet<String>,
    mangle: Option<String>,
}

/// Verify a detached signature against data using a keyring
fn verify_signature(
    sig_data: &[u8],
    data: &[u8],
    keyring_data: &[u8],
) -> Result<bool, Box<dyn std::error::Error>> {
    use openpgp::parse::Parse;
    use openpgp::policy::StandardPolicy;

    let policy = StandardPolicy::new();

    // Parse all certificates from the keyring
    let cert_parser = openpgp::cert::CertParser::from_bytes(keyring_data)?;
    let certs: Vec<_> = cert_parser.filter_map(|r| r.ok()).collect();

    if certs.is_empty() {
        return Err("No valid certificates in keyring".into());
    }

    // Parse the signature
    let packets = openpgp::PacketPile::from_bytes(sig_data)?;

    // Try to verify with each certificate
    for cert in &certs {
        for packet in packets.descendants() {
            let sig = match packet {
                openpgp::Packet::Signature(sig) => sig,
                _ => continue,
            };

            // Check each key in the certificate
            for key_amalg in cert.keys().with_policy(&policy, None) {
                let key_handle = key_amalg.key();
                let key_fingerprint = key_handle.fingerprint();

                // Check if signature issuer matches this key
                let is_issuer = sig.issuer_fingerprints().any(|fp| fp == &key_fingerprint);
                if !is_issuer {
                    continue;
                }

                // Try to verify the signature
                match sig.clone().verify_message(key_handle, data) {
                    Ok(_) => {
                        tracing::debug!(
                            "Signature verified successfully with key {}",
                            key_fingerprint
                        );
                        return Ok(true);
                    }
                    Err(e) => {
                        tracing::debug!("Signature verification failed: {}", e);
                    }
                }
            }
        }
    }

    Ok(false)
}

/// Probe for signature files and verify them
fn probe_signature(
    release: &Release,
    pgpsigurlmangle: Option<&str>,
    keyring_data: &[u8],
) -> Result<Option<SignatureInfo>, Box<dyn std::error::Error>> {
    let mangles: Vec<&str> = if let Some(mangle) = pgpsigurlmangle {
        vec![mangle]
    } else {
        COMMON_PGPSIGURL_MANGLES.to_vec()
    };

    for mangle in mangles {
        let sig_url = if let Some(ref pgpsigurl) = release.pgpsigurl {
            pgpsigurl.clone()
        } else {
            match mangle::apply_mangle(mangle, &release.url) {
                Ok(url) => url,
                Err(e) => {
                    tracing::debug!(
                        "Failed to apply mangle '{}' to '{}': {}",
                        mangle,
                        release.url,
                        e
                    );
                    continue;
                }
            }
        };

        tracing::debug!(
            "Trying signature URL: {} (from release URL: {})",
            sig_url,
            release.url
        );

        // Try to download the signature
        let client = reqwest::blocking::Client::builder()
            .timeout(std::time::Duration::from_secs(30))
            .build()?;

        let sig_response = match client.get(&sig_url).send() {
            Ok(resp) if resp.status().is_success() => {
                tracing::debug!("Successfully downloaded signature from {}", sig_url);
                resp
            }
            Ok(resp) => {
                tracing::debug!(
                    "Signature URL {} returned status {}",
                    sig_url,
                    resp.status()
                );
                continue;
            }
            Err(e) => {
                tracing::debug!("Failed to fetch signature from {}: {}", sig_url, e);
                continue;
            }
        };

        let sig_data = sig_response.bytes()?;

        // Download the actual release file for verification
        let release_data = match release.download_blocking() {
            Ok(data) => {
                tracing::debug!("Downloaded release tarball ({} bytes)", data.len());
                data
            }
            Err(e) => {
                tracing::debug!("Failed to download release: {}", e);
                continue;
            }
        };

        // Parse the signature and extract fingerprints
        use openpgp::parse::Parse;

        let packets = match openpgp::PacketPile::from_bytes(&sig_data) {
            Ok(packets) => packets,
            Err(e) => {
                tracing::debug!("Failed to parse signature packets: {}", e);
                continue;
            }
        };

        // Extract fingerprints from the signature
        let mut fingerprints = Vec::new();
        for packet in packets.descendants() {
            if let openpgp::Packet::Signature(sig) = packet {
                // Try to get the issuer fingerprint
                if let Some(fp) = sig.issuer_fingerprints().next() {
                    let fp_hex = fp.to_hex();
                    tracing::debug!("Found issuer fingerprint in signature: {}", fp_hex);
                    fingerprints.push(fp_hex);
                }
            }
        }

        if fingerprints.is_empty() {
            tracing::debug!("No fingerprints found in signature");
            continue;
        }

        let mut keys = HashSet::new();
        for fp in &fingerprints {
            keys.insert(fp.clone());
        }

        // Try to verify the signature if we have a keyring
        let verification_status = if !keyring_data.is_empty() {
            match verify_signature(&sig_data, &release_data, keyring_data) {
                Ok(true) => {
                    tracing::debug!("Signature verification succeeded");
                    VerificationStatus::Verified
                }
                Ok(false) => {
                    tracing::debug!(
                        "Signature verification failed - signature does not match keyring"
                    );
                    VerificationStatus::Failed
                }
                Err(e) => {
                    tracing::debug!("Error during signature verification: {}", e);
                    // If we can't parse but found fingerprints, treat as unverified
                    VerificationStatus::Unverified
                }
            }
        } else {
            // No keyring available, discovery mode
            tracing::debug!("No keyring available, discovery mode");
            VerificationStatus::Unverified
        };

        tracing::debug!(
            "Found signature with {} key(s), status={:?}",
            keys.len(),
            verification_status
        );
        return Ok(Some(SignatureInfo {
            verification_status,
            keys,
            mangle: Some(mangle.to_string()),
        }));
    }

    Ok(None)
}

/// Analyze used mangles to find common patterns
///
/// Returns (all_mangles, non_none_mangles) where:
/// - all_mangles includes None entries for unsigned releases
/// - non_none_mangles only includes the actual mangle strings
fn analyze_mangles(used_mangles: &[Option<String>]) -> (HashSet<Option<String>>, HashSet<String>) {
    let found_common_mangles: HashSet<Option<String>> =
        used_mangles.iter().take(5).cloned().collect();
    let active_common_mangles: HashSet<String> = found_common_mangles
        .iter()
        .filter_map(|x| x.as_ref().cloned())
        .collect();

    (found_common_mangles, active_common_mangles)
}

/// Determine the pgpmode and description based on found mangles
///
/// Returns (pgpmode, description):
/// - If all releases are signed (only one entry, which is Some): ("mangle", "Check upstream PGP signatures.")
/// - Otherwise: ("auto", "Opportunistically check upstream PGP signatures.")
fn determine_pgpmode(
    found_common_mangles: &HashSet<Option<String>>,
) -> (debian_watch::PgpMode, String) {
    if found_common_mangles.len() == 1 {
        (
            debian_watch::PgpMode::Mangle,
            "Check upstream PGP signatures.".to_string(),
        )
    } else {
        (
            debian_watch::PgpMode::Auto,
            "Opportunistically check upstream PGP signatures.".to_string(),
        )
    }
}

/// Export a certificate in minimal armored format
fn export_cert_armored(cert: &openpgp::Cert) -> Result<Vec<u8>, String> {
    use openpgp::serialize::Serialize;

    let mut key_output = Vec::new();
    {
        let mut writer =
            openpgp::armor::Writer::new(&mut key_output, openpgp::armor::Kind::PublicKey)
                .map_err(|e| format!("Failed to create armor writer: {}", e))?;

        cert.serialize(&mut writer)
            .map_err(|e| format!("Failed to serialize certificate: {}", e))?;

        writer
            .finalize()
            .map_err(|e| format!("Failed to finalize armor: {}", e))?;
    }

    Ok(key_output)
}

pub fn detect(
    ws: &dyn Workspace,
    preferences: &FixerPreferences,
) -> Result<Vec<Diagnostic>, FixerError> {
    let package = ws.package().unwrap_or("").to_string();
    tracing::debug!("Running pubkey detect for package {}", package);

    let watch_rel = PathBuf::from("debian/watch");
    let watch_file = match ws.parsed_watch() {
        Ok(w) => w,
        Err(debian_workspace::Error::NotFound) => {
            tracing::debug!("No debian/watch file found");
            return Ok(Vec::new());
        }
        Err(e) => {
            return Err(FixerError::Other(format!(
                "Failed to parse debian/watch: {}",
                e
            )))
        }
    };

    // Network is required for both signature probing and key fetching.
    if !preferences.net_access.unwrap_or(false) {
        tracing::debug!("Network access not enabled, skipping");
        return Ok(Vec::new());
    }

    // Load existing keyring if available; the first present file wins.
    let (has_keys, keyring_data): (bool, Vec<u8>) = {
        let mut found = None;
        for path in &[
            "debian/upstream/signing-key.asc",
            "debian/upstream/signing-key.pgp",
        ] {
            if let Some(data) = ws.read_file(Path::new(path))? {
                tracing::debug!("Loaded existing keyring from {}", path);
                found = Some(data);
                break;
            }
        }
        match found {
            Some(d) => (true, d.into_owned()),
            None => (false, Vec::new()),
        }
    };

    let mut needed_keys: HashSet<String> = HashSet::new();
    let mut description: Option<String> = None;
    let mut diagnostics: Vec<Diagnostic> = Vec::new();
    // Watch file edits produced during entry analysis. We collect them per
    // diagnostic so override gating works correctly.
    let mut watch_actions: Vec<Action> = Vec::new();

    for entry in watch_file.entries() {
        let pgpsigurlmangle = entry.get_option("pgpsigurlmangle");

        // Skip entries that already have pgpsigurlmangle and keys
        if pgpsigurlmangle.is_some() && has_keys {
            tracing::debug!("Entry already has pgpsigurlmangle and keys, skipping");
            continue;
        }

        let pgpmode = entry
            .get_option("pgpmode")
            .unwrap_or_else(|| "default".to_string());

        // Skip if pgpmode is already set and diligence is 0
        if entry.get_option("pgpmode").is_some() && preferences.diligence.unwrap_or(0) == 0 {
            tracing::debug!("pgpmode already set and diligence=0, skipping");
            continue;
        }

        // Skip certain pgpmodes that we can't handle
        if matches!(pgpmode.as_str(), "gittag" | "previous" | "next" | "self") {
            tracing::debug!("Unsupported pgpmode: {}, skipping", pgpmode);
            return Ok(Vec::new());
        }

        // Discover releases
        tracing::debug!("Discovering releases for package {}", package);
        let releases = match entry.discover_blocking(|| package.to_string()) {
            Ok(mut rels) => {
                rels.sort_by(|a, b| b.cmp(a)); // Sort in reverse order (newest first)
                tracing::debug!("Found {} releases", rels.len());
                rels
            }
            Err(e) => {
                if matches!(e, debian_watch::discover::DiscoveryError::HttpError(_)) {
                    tracing::debug!("HTTP error accessing discovery URL: {}", e);
                    return Ok(Vec::new());
                }
                return Err(FixerError::Other(format!(
                    "Failed to discover releases: {}",
                    e
                )));
            }
        };

        let mut verification_statuses = Vec::new();
        let mut used_mangles: Vec<Option<String>> = Vec::new();
        let mut has_verification_failure = false;

        tracing::debug!(
            "Checking signatures for up to {} releases",
            RELEASES_TO_INSPECT
        );
        for release in releases.iter().take(RELEASES_TO_INSPECT) {
            tracing::debug!("Probing signature for release {}", release.version);
            match probe_signature(release, pgpsigurlmangle.as_deref(), &keyring_data) {
                Ok(Some(sig_info)) => {
                    tracing::debug!(
                        "Found signature with mangle: {:?}, status: {:?}",
                        sig_info.mangle,
                        sig_info.verification_status
                    );

                    // Check for verification failures
                    if matches!(sig_info.verification_status, VerificationStatus::Failed) {
                        has_verification_failure = true;
                    }

                    verification_statuses.push(sig_info.verification_status);
                    used_mangles.push(sig_info.mangle.clone());
                    needed_keys.extend(sig_info.keys);
                }
                Ok(None) => {
                    tracing::debug!("No signature found for release {}", release.version);
                    used_mangles.push(None);
                }
                Err(e) => {
                    tracing::debug!("Error probing signature: {}", e);
                    used_mangles.push(None);
                }
            }
        }

        // If we have an existing keyring and signatures failed verification, skip this entry
        if has_keys && has_verification_failure {
            tracing::warn!(
                "Signatures do not match existing keyring at debian/upstream/signing-key.*. \
                 Not updating watch file or fetching different keys. \
                 If upstream changed their signing key, manually update the keyring."
            );
            return Ok(Vec::new());
        }

        // For unverified signatures (discovery mode), we need enough successful probes
        let successful_probes = verification_statuses.len();
        if successful_probes < NUM_KEYS_TO_CHECK.min(releases.len()) {
            tracing::debug!(
                "Not enough signatures found ({} < {}), skipping",
                successful_probes,
                NUM_KEYS_TO_CHECK
            );
            return Ok(Vec::new());
        }

        let (found_common_mangles, active_common_mangles) = analyze_mangles(&used_mangles);

        tracing::debug!(
            "Found {} common mangles, {} active",
            found_common_mangles.len(),
            active_common_mangles.len()
        );

        if pgpsigurlmangle.is_none() && !active_common_mangles.is_empty() {
            let entry_url = entry.url();
            let mut entry_actions: Vec<Action> = Vec::new();

            // If only a single mangle is used for all releases that have
            // signatures, set that.
            if active_common_mangles.len() == 1 {
                let new_mangle = active_common_mangles.iter().next().unwrap().clone();
                tracing::debug!("Setting pgpsigurlmangle to: {}", new_mangle);
                entry_actions.push(Action::Watch(WatchAction::SetEntryOption {
                    file: watch_rel.clone(),
                    url: entry_url.clone(),
                    option: "pgpsigurlmangle".into(),
                    value: new_mangle,
                }));
            }

            let (pgpmode_value, mut desc) = determine_pgpmode(&found_common_mangles);
            tracing::debug!("Setting pgpmode to: {:?}", pgpmode_value);
            entry_actions.push(Action::Watch(WatchAction::SetEntryOption {
                file: watch_rel.clone(),
                url: entry_url,
                option: "pgpmode".into(),
                value: pgpmode_value.to_string(),
            }));

            // Include fingerprints in description if we found any.
            if !needed_keys.is_empty() {
                let fingerprints: Vec<String> = needed_keys.iter().cloned().collect();
                desc = format!(
                    "{} ({})",
                    desc.trim_end_matches('.'),
                    fingerprints.join(", ")
                );
            }
            description = Some(desc.clone());

            let issue = LintianIssue::source_with_info(
                "debian-watch-does-not-check-openpgp-signature",
                Visibility::Pedantic,
                vec!["[debian/watch]".to_string()],
            );
            watch_actions.extend(entry_actions.iter().cloned());
            diagnostics.push(
                Diagnostic::with_actions(
                    issue,
                    "debian/watch does not check the OpenPGP signature.",
                    desc,
                    entry_actions,
                )
                .with_certainty(Certainty::Certain),
            );
        }
    }
    let _ = watch_actions;

    if !has_keys && !needed_keys.is_empty() {
        tracing::debug!("Need to fetch {} keys", needed_keys.len());

        // Fetch and export keys using sequoia.
        let mut keyfile_content = Vec::new();
        let keys_vec: Vec<String> = needed_keys.iter().cloned().collect();

        // KEYSERVER is a lintian-brush-internal override (used by tests to
        // point at a local server); it is not a standard environment
        // variable, so we only honour preferences.extra_env.
        let keyserver = preferences
            .extra_env
            .as_ref()
            .and_then(|e| e.get("KEYSERVER").cloned())
            .unwrap_or_else(|| "https://keys.openpgp.org".to_string());

        let mut fetch_failed = false;
        for fingerprint in &keys_vec {
            tracing::debug!("Fetching key with fingerprint: {}", fingerprint);
            let url = format!("{}/vks/v1/by-fingerprint/{}", keyserver, fingerprint);

            let client = reqwest::blocking::Client::builder()
                .timeout(std::time::Duration::from_secs(30))
                .build()
                .map_err(|e| FixerError::Other(format!("Failed to build HTTP client: {}", e)))?;

            let response = match client.get(&url).send() {
                Ok(resp) if resp.status().is_success() => resp,
                Ok(resp) => {
                    tracing::debug!(
                        "Keyserver returned status {} for key {}",
                        resp.status(),
                        fingerprint
                    );
                    fetch_failed = true;
                    break;
                }
                Err(e) => {
                    tracing::debug!("Failed to fetch key {}: {}", fingerprint, e);
                    fetch_failed = true;
                    break;
                }
            };

            let key_data = response
                .bytes()
                .map_err(|e| FixerError::Other(format!("Failed to read key data: {}", e)))?;

            use openpgp::parse::Parse;
            let cert = openpgp::Cert::from_reader(std::io::Cursor::new(&key_data[..]))
                .map_err(|e| FixerError::Other(format!("Failed to parse certificate: {}", e)))?;

            let key_output = export_cert_armored(&cert).map_err(FixerError::Other)?;
            keyfile_content.extend_from_slice(&key_output);
            keyfile_content.push(b'\n');
        }

        if !fetch_failed && !keyfile_content.is_empty() {
            let issue = LintianIssue::source_with_info(
                "debian-watch-file-pubkey-file-is-missing",
                Visibility::Error,
                vec!["[debian/watch]".to_string()],
            );
            let key_desc = format!(
                "Add upstream signing keys ({}).",
                needed_keys.iter().cloned().collect::<Vec<_>>().join(", ")
            );
            if description.is_none() {
                description = Some(key_desc.clone());
            }
            diagnostics.push(
                Diagnostic::with_actions(
                    issue,
                    "debian/watch references signing keys that are not present.",
                    key_desc,
                    vec![Action::Filesystem(FilesystemAction::Write {
                        file: PathBuf::from("debian/upstream/signing-key.asc"),
                        content: keyfile_content,
                    })],
                )
                .with_certainty(Certainty::Certain),
            );
        }
    }

    let _ = description; // formerly used to override the framework's describer
    Ok(diagnostics)
}

declare_detector! {
    name: "pubkey",
    tags: [
        "debian-watch-does-not-check-openpgp-signature",
        "debian-watch-file-pubkey-file-is-missing"
    ],
    triggers: [
        debian_workspace::Trigger::Watch(debian_workspace::WatchAspect::Source),
        debian_workspace::Trigger::Watch(debian_workspace::WatchAspect::Option(
            "pgpsigurlmangle",
        )),
        debian_workspace::Trigger::Watch(debian_workspace::WatchAspect::Option(
            "pgpmode",
        )),
        debian_workspace::Trigger::File("debian/upstream/signing-key.asc"),
        debian_workspace::Trigger::File("debian/upstream/signing-key.pgp"),
    ],
    cost: crate::detector::DetectorCost::Network,
    detect: |ws, prefs| detect(ws, prefs),
}

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

    #[test]
    fn test_common_mangles() {
        assert!(COMMON_PGPSIGURL_MANGLES.contains(&"s/$/.asc/"));
        assert!(COMMON_PGPSIGURL_MANGLES.contains(&"s/$/.sig/"));
        assert!(COMMON_PGPSIGURL_MANGLES.contains(&"s/$/.gpg/"));
        assert_eq!(COMMON_PGPSIGURL_MANGLES.len(), 5);
    }

    #[test]
    fn test_analyze_mangles_all_same() {
        let mangles = vec![
            Some("s/$/.asc/".to_string()),
            Some("s/$/.asc/".to_string()),
            Some("s/$/.asc/".to_string()),
        ];
        let (found, active) = analyze_mangles(&mangles);

        assert_eq!(found.len(), 1);
        assert!(found.contains(&Some("s/$/.asc/".to_string())));
        assert_eq!(active.len(), 1);
        assert!(active.contains("s/$/.asc/"));
    }

    #[test]
    fn test_analyze_mangles_mixed() {
        let mangles = vec![
            Some("s/$/.asc/".to_string()),
            None,
            Some("s/$/.asc/".to_string()),
        ];
        let (found, active) = analyze_mangles(&mangles);

        assert_eq!(found.len(), 2); // Some and None
        assert!(found.contains(&Some("s/$/.asc/".to_string())));
        assert!(found.contains(&None));
        assert_eq!(active.len(), 1); // Only the Some variant
        assert!(active.contains("s/$/.asc/"));
    }

    #[test]
    fn test_analyze_mangles_all_none() {
        let mangles = vec![None, None, None];
        let (found, active) = analyze_mangles(&mangles);

        assert_eq!(found.len(), 1);
        assert!(found.contains(&None));
        assert_eq!(active.len(), 0);
    }

    #[test]
    fn test_analyze_mangles_different_mangles() {
        let mangles = vec![
            Some("s/$/.asc/".to_string()),
            Some("s/$/.sig/".to_string()),
            Some("s/$/.asc/".to_string()),
        ];
        let (found, active) = analyze_mangles(&mangles);

        assert_eq!(found.len(), 2);
        assert_eq!(active.len(), 2);
        assert!(active.contains("s/$/.asc/"));
        assert!(active.contains("s/$/.sig/"));
    }

    #[test]
    fn test_determine_pgpmode_all_signed() {
        let mut mangles = HashSet::new();
        mangles.insert(Some("s/$/.asc/".to_string()));

        let (mode, desc) = determine_pgpmode(&mangles);
        assert_eq!(mode, debian_watch::PgpMode::Mangle);
        assert_eq!(desc, "Check upstream PGP signatures.");
    }

    #[test]
    fn test_determine_pgpmode_mixed() {
        let mut mangles = HashSet::new();
        mangles.insert(Some("s/$/.asc/".to_string()));
        mangles.insert(None);

        let (mode, desc) = determine_pgpmode(&mangles);
        assert_eq!(mode, debian_watch::PgpMode::Auto);
        assert_eq!(desc, "Opportunistically check upstream PGP signatures.");
    }

    #[test]
    fn test_determine_pgpmode_multiple_mangles() {
        let mut mangles = HashSet::new();
        mangles.insert(Some("s/$/.asc/".to_string()));
        mangles.insert(Some("s/$/.sig/".to_string()));

        let (mode, desc) = determine_pgpmode(&mangles);
        assert_eq!(mode, debian_watch::PgpMode::Auto);
        assert_eq!(desc, "Opportunistically check upstream PGP signatures.");
    }

    #[test]
    fn test_verify_signature_with_empty_keyring() {
        let sig_data = b"fake signature data";
        let data = b"fake release data";
        let keyring_data = b"";

        let result = verify_signature(sig_data, data, keyring_data);
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("No valid certificates"));
    }

    #[test]
    fn test_verify_signature_with_invalid_keyring() {
        let sig_data = b"fake signature data";
        let data = b"fake release data";
        let keyring_data = b"not a valid keyring";

        let result = verify_signature(sig_data, data, keyring_data);
        assert!(result.is_err());
    }

    #[test]
    fn test_export_cert_armored_with_test_key() {
        use openpgp::cert::CertBuilder;

        // Generate a test certificate
        let (cert, _) = CertBuilder::new()
            .add_userid("Test User <test@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate");

        // Export it
        let result = export_cert_armored(&cert);
        assert!(result.is_ok());

        let exported = result.unwrap();
        let exported_str = String::from_utf8_lossy(&exported);

        // Check that it's armored
        assert!(exported_str.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----"));
        assert!(exported_str.contains("-----END PGP PUBLIC KEY BLOCK-----"));
    }

    #[test]
    fn test_verify_signature_roundtrip() {
        use openpgp::cert::CertBuilder;
        use openpgp::policy::StandardPolicy;
        use openpgp::serialize::stream::*;

        let policy = StandardPolicy::new();

        // Generate a test certificate with signing capability
        let (cert, _) = CertBuilder::new()
            .add_userid("Test User <test@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate");

        // Get the signing keypair
        let keypair = cert
            .keys()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .expect("No signing key found")
            .key()
            .clone()
            .into_keypair()
            .expect("Failed to convert to keypair");

        // Data to sign
        let data = b"Hello, world!";

        // Create a detached signature
        let mut sig_data = Vec::new();
        {
            let message = Message::new(&mut sig_data);
            let signer = Signer::new(message, keypair)
                .expect("Failed to create signer")
                .detached()
                .build()
                .expect("Failed to build signer");

            let mut writer = signer;
            std::io::copy(&mut std::io::Cursor::new(data), &mut writer)
                .expect("Failed to write data");
            writer.finalize().expect("Failed to finalize signature");
        }

        // Export the certificate as keyring
        let keyring_data = export_cert_armored(&cert).expect("Failed to export cert");

        // Verify the signature
        let result = verify_signature(&sig_data, data, &keyring_data);
        assert_eq!(result.unwrap(), true);
    }

    #[test]
    fn test_verify_signature_wrong_data() {
        use openpgp::cert::CertBuilder;
        use openpgp::policy::StandardPolicy;
        use openpgp::serialize::stream::*;

        let policy = StandardPolicy::new();

        // Generate a test certificate with signing capability
        let (cert, _) = CertBuilder::new()
            .add_userid("Test User <test@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate");

        let keypair = cert
            .keys()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .expect("No signing key found")
            .key()
            .clone()
            .into_keypair()
            .expect("Failed to convert to keypair");

        let data = b"Hello, world!";

        // Create a detached signature
        let mut sig_data = Vec::new();
        {
            let message = Message::new(&mut sig_data);
            let signer = Signer::new(message, keypair)
                .expect("Failed to create signer")
                .detached()
                .build()
                .expect("Failed to build signer");

            let mut writer = signer;
            std::io::copy(&mut std::io::Cursor::new(data), &mut writer)
                .expect("Failed to write data");
            writer.finalize().expect("Failed to finalize signature");
        }

        let keyring_data = export_cert_armored(&cert).expect("Failed to export cert");

        // Try to verify with different data
        let wrong_data = b"Different data!";
        let result = verify_signature(&sig_data, wrong_data, &keyring_data);
        assert_eq!(result.unwrap(), false);
    }

    #[test]
    fn test_verification_status_unverified_when_no_keyring() {
        use openpgp::cert::CertBuilder;
        use openpgp::policy::StandardPolicy;
        use openpgp::serialize::stream::*;

        let policy = StandardPolicy::new();

        // Generate a test certificate
        let (cert, _) = CertBuilder::new()
            .add_userid("Test User <test@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate");

        let keypair = cert
            .keys()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .expect("No signing key found")
            .key()
            .clone()
            .into_keypair()
            .expect("Failed to convert to keypair");

        let data = b"Hello, world!";

        // Create a detached signature
        let mut sig_data = Vec::new();
        {
            let message = Message::new(&mut sig_data);
            let signer = Signer::new(message, keypair)
                .expect("Failed to create signer")
                .detached()
                .build()
                .expect("Failed to build signer");

            let mut writer = signer;
            std::io::copy(&mut std::io::Cursor::new(data), &mut writer)
                .expect("Failed to write data");
            writer.finalize().expect("Failed to finalize signature");
        }

        // Create a mock Release (we'll use a simplified approach for testing)
        // Since we can't easily mock Release, we test verify_signature directly

        // Empty keyring should result in unverified status
        let empty_keyring = b"";

        // Since probe_signature needs a Release, let's just verify the verify_signature behavior
        // When keyring is empty, probe_signature returns Unverified status
        // This is tested indirectly through the verify_signature error
        let result = verify_signature(&sig_data, data, empty_keyring);
        assert!(result.is_err());
        assert!(result
            .unwrap_err()
            .to_string()
            .contains("No valid certificates"));
    }

    #[test]
    fn test_verification_status_verified_with_correct_keyring() {
        use openpgp::cert::CertBuilder;
        use openpgp::policy::StandardPolicy;
        use openpgp::serialize::stream::*;

        let policy = StandardPolicy::new();

        // Generate a test certificate
        let (cert, _) = CertBuilder::new()
            .add_userid("Test User <test@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate");

        let keypair = cert
            .keys()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .expect("No signing key found")
            .key()
            .clone()
            .into_keypair()
            .expect("Failed to convert to keypair");

        let data = b"Hello, world!";

        // Create a detached signature
        let mut sig_data = Vec::new();
        {
            let message = Message::new(&mut sig_data);
            let signer = Signer::new(message, keypair)
                .expect("Failed to create signer")
                .detached()
                .build()
                .expect("Failed to build signer");

            let mut writer = signer;
            std::io::copy(&mut std::io::Cursor::new(data), &mut writer)
                .expect("Failed to write data");
            writer.finalize().expect("Failed to finalize signature");
        }

        let keyring_data = export_cert_armored(&cert).expect("Failed to export cert");

        // Verify with correct keyring should return true (Verified status)
        let result = verify_signature(&sig_data, data, &keyring_data);
        assert!(result.unwrap());
    }

    #[test]
    fn test_verification_status_failed_with_wrong_keyring() {
        use openpgp::cert::CertBuilder;
        use openpgp::policy::StandardPolicy;
        use openpgp::serialize::stream::*;

        let policy = StandardPolicy::new();

        // Generate two different certificates
        let (cert1, _) = CertBuilder::new()
            .add_userid("Test User 1 <test1@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate 1");

        let (cert2, _) = CertBuilder::new()
            .add_userid("Test User 2 <test2@example.com>")
            .add_signing_subkey()
            .generate()
            .expect("Failed to generate test certificate 2");

        // Sign with cert1
        let keypair1 = cert1
            .keys()
            .with_policy(&policy, None)
            .alive()
            .revoked(false)
            .for_signing()
            .secret()
            .next()
            .expect("No signing key found")
            .key()
            .clone()
            .into_keypair()
            .expect("Failed to convert to keypair");

        let data = b"Hello, world!";

        // Create a detached signature with cert1
        let mut sig_data = Vec::new();
        {
            let message = Message::new(&mut sig_data);
            let signer = Signer::new(message, keypair1)
                .expect("Failed to create signer")
                .detached()
                .build()
                .expect("Failed to build signer");

            let mut writer = signer;
            std::io::copy(&mut std::io::Cursor::new(data), &mut writer)
                .expect("Failed to write data");
            writer.finalize().expect("Failed to finalize signature");
        }

        // Export cert2 as keyring (different key!)
        let wrong_keyring = export_cert_armored(&cert2).expect("Failed to export cert");

        // Verify with wrong keyring should return false (Failed status)
        let result = verify_signature(&sig_data, data, &wrong_keyring);
        assert!(!result.unwrap());
    }
}
