use runx_contracts::Receipt;

use super::ReceiptVerification;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ReceiptProofStatusKind {
    Verified,
    Failed,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReceiptProofFindingSummary {
    pub code: String,
    pub path: String,
    pub message: String,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReceiptProofStatus {
    pub receipt_id: String,
    pub status: ReceiptProofStatusKind,
    pub finding_summaries: Vec<ReceiptProofFindingSummary>,
}

#[must_use]
pub fn receipt_proof_status(
    receipt: &Receipt,
    verification: &ReceiptVerification,
) -> ReceiptProofStatus {
    ReceiptProofStatus {
        receipt_id: receipt.id.to_string(),
        status: if verification.valid {
            ReceiptProofStatusKind::Verified
        } else {
            ReceiptProofStatusKind::Failed
        },
        finding_summaries: verification
            .findings
            .iter()
            .map(|finding| ReceiptProofFindingSummary {
                code: format!("{:?}", finding.code),
                path: public_text(&finding.path),
                message: public_text(&finding.message),
            })
            .collect(),
    }
}

fn public_text(value: &str) -> String {
    value
        .split_whitespace()
        .map(redact_public_token)
        .collect::<Vec<_>>()
        .join(" ")
}

fn redact_public_token(token: &str) -> &str {
    let core = token.trim_matches(|character: char| {
        matches!(
            character,
            '(' | ')' | '[' | ']' | '{' | '}' | '"' | '\'' | ',' | ';'
        )
    });
    if looks_like_local_path(core) {
        "[local-path]"
    } else if looks_like_secret_value(core) {
        "[secret]"
    } else {
        token
    }
}

fn looks_like_local_path(token: &str) -> bool {
    token.starts_with('/') || token.as_bytes().get(1..3) == Some(b":\\")
}

fn looks_like_secret_value(token: &str) -> bool {
    token.len() >= 32
        && token
            .bytes()
            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'/' | b'='))
        && token.bytes().any(|byte| matches!(byte, b'+' | b'/' | b'='))
}
