mod archive;
mod html;
mod pdf;

use compliance_core::models::dast::DastFinding;
use compliance_core::models::finding::Finding;
use compliance_core::models::pentest::{
    AttackChainNode, CodeContextHint, PentestConfig, PentestSession,
};
use compliance_core::models::sbom::SbomEntry;
use sha2::{Digest, Sha256};

/// Report archive with metadata
pub struct ReportArchive {
    /// The password-protected ZIP bytes
    pub archive: Vec<u8>,
    /// SHA-256 hex digest of the archive
    pub sha256: String,
}

/// Report context gathered from the database
pub struct ReportContext {
    pub session: PentestSession,
    pub target_name: String,
    pub target_url: String,
    pub findings: Vec<DastFinding>,
    pub attack_chain: Vec<AttackChainNode>,
    pub requester_name: String,
    pub requester_email: String,
    pub config: Option<PentestConfig>,
    /// SAST findings for the linked repository (for code-level correlation)
    pub sast_findings: Vec<Finding>,
    /// Vulnerable dependencies from SBOM
    pub sbom_entries: Vec<SbomEntry>,
    /// Code knowledge graph entry points linked to SAST findings
    pub code_context: Vec<CodeContextHint>,
}

/// Generate a password-protected ZIP archive containing the pentest report.
///
/// The archive contains:
/// - `report.pdf` — Professional pentest report (PDF)
/// - `report.html` — HTML source (fallback)
/// - `findings.json` — Raw findings data
/// - `attack-chain.json` — Attack chain timeline
///
/// Files are encrypted with AES-256 inside the ZIP (standard WinZip AES format,
/// supported by 7-Zip, WinRAR, macOS Archive Utility, etc.).
pub async fn generate_encrypted_report(
    ctx: &ReportContext,
    password: &str,
) -> Result<ReportArchive, String> {
    let html = html::build_html_report(ctx);

    // Convert HTML to PDF via headless Chrome
    let pdf_bytes = pdf::html_to_pdf(&html).await?;

    let zip_bytes = archive::build_zip(ctx, password, &html, &pdf_bytes)
        .map_err(|e| format!("Failed to create archive: {e}"))?;

    let mut hasher = Sha256::new();
    hasher.update(&zip_bytes);
    let sha256 = hex::encode(hasher.finalize());

    Ok(ReportArchive {
        archive: zip_bytes,
        sha256,
    })
}
