use serde::{Deserialize, Serialize};

use super::{QueueDiagnostics, ReleaseSecurityAuditMode, ReplayReport};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseCheckDocument {
    pub version: u32,
    pub check_id: String,
    pub created_at: String,
    pub gate_profile: String,
    pub status: ReleaseGateStatus,
    pub passed: bool,
    pub reasons: Vec<String>,
    pub thresholds: ReleaseCheckThresholds,
    pub run_summary: ReleaseCheckRunSummary,
    pub embedding: ReleaseCheckEmbeddingMetadata,
    pub gate_record_uri: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseCheckThresholds {
    pub threshold_p95_ms: u128,
    pub min_top1_accuracy: f32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub min_stress_top1_accuracy: Option<f32>,
    pub max_p95_regression_pct: Option<f32>,
    pub max_top1_regression_pct: Option<f32>,
    pub window_size: usize,
    pub required_passes: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseCheckRunSummary {
    pub evaluated_runs: usize,
    pub passing_runs: usize,
    pub latest_report_uri: Option<String>,
    pub previous_report_uri: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub latest_p95_latency_us: Option<u128>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub previous_p95_latency_us: Option<u128>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseCheckEmbeddingMetadata {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub embedding_provider: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub embedding_strict_error: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyInventorySummary {
    pub lockfile_present: bool,
    pub package_count: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyAuditSummary {
    pub tool: String,
    pub mode: ReleaseSecurityAuditMode,
    pub available: bool,
    pub executed: bool,
    pub status: DependencyAuditStatus,
    pub advisories_found: usize,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tool_version: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub output_excerpt: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAuditCheck {
    pub name: String,
    pub passed: bool,
    pub details: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAuditReport {
    pub report_id: String,
    pub created_at: String,
    pub workspace_dir: String,
    pub passed: bool,
    pub status: EvidenceStatus,
    pub inventory: DependencyInventorySummary,
    pub dependency_audit: DependencyAuditSummary,
    pub checks: Vec<SecurityAuditCheck>,
    pub report_uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperabilityEvidenceCheck {
    pub name: String,
    pub passed: bool,
    pub details: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperabilitySampleWindow {
    pub trace_limit: usize,
    pub request_limit: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperabilityCoverage {
    pub traces_analyzed: usize,
    pub request_logs_scanned: usize,
    pub trace_metrics_snapshot_uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperabilityEvidenceReport {
    pub report_id: String,
    pub created_at: String,
    pub passed: bool,
    pub status: EvidenceStatus,
    pub sample_window: OperabilitySampleWindow,
    pub coverage: OperabilityCoverage,
    pub queue: QueueDiagnostics,
    pub checks: Vec<OperabilityEvidenceCheck>,
    pub report_uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityEvidenceCheck {
    pub name: String,
    pub passed: bool,
    pub details: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityReplayPlan {
    pub replay_limit: usize,
    pub max_cycles: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityReplayProgress {
    pub replay_cycles: u32,
    pub replay_totals: ReplayReport,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityQueueDelta {
    pub baseline_dead_letter: u64,
    pub final_dead_letter: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub baseline_checkpoint: Option<i64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub final_checkpoint: Option<i64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilitySearchProbe {
    pub queued_root_uri: String,
    pub query: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub replay_hit_uri: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub restart_hit_uri: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityEvidenceReport {
    pub report_id: String,
    pub created_at: String,
    pub passed: bool,
    pub status: EvidenceStatus,
    pub replay_plan: ReliabilityReplayPlan,
    pub replay_progress: ReliabilityReplayProgress,
    pub queue_delta: ReliabilityQueueDelta,
    pub search_probe: ReliabilitySearchProbe,
    pub queue: QueueDiagnostics,
    pub checks: Vec<ReliabilityEvidenceCheck>,
    pub report_uri: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommandProbeResult {
    pub test_name: String,
    pub command_ok: bool,
    pub matched: bool,
    pub output_excerpt: String,
    pub passed: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EpisodicSemverProbeResult {
    pub passed: bool,
    pub error: Option<String>,
    pub manifest_req: Option<String>,
    pub manifest_req_ok: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub manifest_path: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub manifest_path_ok: Option<bool>,
    pub manifest_uses_path: Option<bool>,
    pub manifest_uses_git: Option<bool>,
    pub manifest_source_ok: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace_member_path: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace_member_present: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace_version: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace_version_ok: Option<bool>,
    pub lock_version: Option<String>,
    pub lock_version_ok: Option<bool>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub lock_source: Option<String>,
    pub lock_source_ok: Option<bool>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EpisodicSemverPolicy {
    pub required_major: u64,
    pub required_minor: u64,
    pub required_manifest_path: String,
    pub required_workspace_member: String,
    pub allowed_manifest_operators: Vec<String>,
    pub required_lock_source: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OntologyContractPolicy {
    pub schema_uri: String,
    pub required_schema_version: u32,
    pub probe_test_name: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OntologySchemaVersionProbe {
    pub schema_uri: String,
    pub schema_version: Option<u32>,
    pub schema_version_ok: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OntologySchemaCardinality {
    pub object_type_count: usize,
    pub link_type_count: usize,
    pub action_type_count: usize,
    pub invariant_count: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OntologyInvariantCheckSummary {
    pub passed: usize,
    pub failed: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OntologyContractProbeResult {
    pub passed: bool,
    pub error: Option<String>,
    pub command_probe: CommandProbeResult,
    pub schema: OntologySchemaVersionProbe,
    pub cardinality: OntologySchemaCardinality,
    pub invariant_checks: OntologyInvariantCheckSummary,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractIntegrityGateDetails {
    pub policy: EpisodicSemverPolicy,
    pub contract_probe: CommandProbeResult,
    pub episodic_api_probe: CommandProbeResult,
    pub episodic_semver_probe: EpisodicSemverProbeResult,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ontology_policy: Option<OntologyContractPolicy>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ontology_probe: Option<OntologyContractProbeResult>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityGateDetails {
    pub status: EvidenceStatus,
    pub replay_done: usize,
    pub dead_letter: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalQualityGateDetails {
    pub executed_cases: usize,
    pub top1_accuracy: f32,
    pub min_top1_accuracy: f32,
    pub failed: usize,
    pub filter_ignored: usize,
    pub relation_missing: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionMemoryGateDetails {
    pub base_details: String,
    pub memory_category_miss: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SecurityAuditGateDetails {
    pub status: EvidenceStatus,
    pub mode: ReleaseSecurityAuditMode,
    pub strict_mode_required: bool,
    pub strict_mode: bool,
    pub audit_status: DependencyAuditStatus,
    pub advisories_found: usize,
    pub packages: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkGateDetails {
    pub passed: bool,
    pub evaluated_runs: usize,
    pub passing_runs: usize,
    pub reasons: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OperabilityGateDetails {
    pub status: EvidenceStatus,
    pub traces_analyzed: usize,
    pub request_logs_scanned: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockerRollupGateDetails {
    pub unresolved_blockers: usize,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildQualityGateDetails {
    pub cargo_check: bool,
    pub cargo_fmt: bool,
    pub cargo_clippy: bool,
    pub check_output: String,
    pub fmt_output: String,
    pub clippy_output: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReleaseGateId {
    #[serde(rename = "G0")]
    ContractIntegrity,
    #[serde(rename = "G1")]
    BuildQuality,
    #[serde(rename = "G2")]
    ReliabilityEvidence,
    #[serde(rename = "G3")]
    EvalQuality,
    #[serde(rename = "G4")]
    SessionMemory,
    #[serde(rename = "G5")]
    SecurityAudit,
    #[serde(rename = "G6")]
    Benchmark,
    #[serde(rename = "G7")]
    OperabilityEvidence,
    #[serde(rename = "G8")]
    BlockerRollup,
}

impl ReleaseGateId {
    pub const ALL: [Self; 9] = [
        Self::ContractIntegrity,
        Self::BuildQuality,
        Self::ReliabilityEvidence,
        Self::EvalQuality,
        Self::SessionMemory,
        Self::SecurityAudit,
        Self::Benchmark,
        Self::OperabilityEvidence,
        Self::BlockerRollup,
    ];

    pub const fn code(self) -> &'static str {
        match self {
            Self::ContractIntegrity => "G0",
            Self::BuildQuality => "G1",
            Self::ReliabilityEvidence => "G2",
            Self::EvalQuality => "G3",
            Self::SessionMemory => "G4",
            Self::SecurityAudit => "G5",
            Self::Benchmark => "G6",
            Self::OperabilityEvidence => "G7",
            Self::BlockerRollup => "G8",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReleaseGateStatus {
    Pass,
    Fail,
}

impl ReleaseGateStatus {
    pub const fn from_passed(passed: bool) -> Self {
        if passed { Self::Pass } else { Self::Fail }
    }

    pub const fn as_str(self) -> &'static str {
        match self {
            Self::Pass => "pass",
            Self::Fail => "fail",
        }
    }
}

impl std::fmt::Display for ReleaseGateStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.as_str())
    }
}

pub type EvidenceStatus = ReleaseGateStatus;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DependencyAuditStatus {
    Passed,
    VulnerabilitiesFound,
    ToolMissing,
    Error,
    HostToolsDisabled,
}

impl std::fmt::Display for DependencyAuditStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let value = match self {
            Self::Passed => "passed",
            Self::VulnerabilitiesFound => "vulnerabilities_found",
            Self::ToolMissing => "tool_missing",
            Self::Error => "error",
            Self::HostToolsDisabled => "host_tools_disabled",
        };
        f.write_str(value)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", content = "data", rename_all = "snake_case")]
pub enum ReleaseGateDetails {
    ContractIntegrity(Box<ContractIntegrityGateDetails>),
    BuildQuality(BuildQualityGateDetails),
    ReliabilityEvidence(ReliabilityGateDetails),
    EvalQuality(EvalQualityGateDetails),
    SessionMemory(SessionMemoryGateDetails),
    SecurityAudit(SecurityAuditGateDetails),
    Benchmark(BenchmarkGateDetails),
    OperabilityEvidence(OperabilityGateDetails),
    BlockerRollup(BlockerRollupGateDetails),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseGateDecision {
    pub gate_id: ReleaseGateId,
    pub passed: bool,
    pub status: ReleaseGateStatus,
    pub details: ReleaseGateDetails,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub evidence_uri: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseGatePackReport {
    pub pack_id: String,
    pub created_at: String,
    pub workspace_dir: String,
    pub passed: bool,
    pub status: ReleaseGateStatus,
    pub unresolved_blockers: usize,
    pub decisions: Vec<ReleaseGateDecision>,
    pub report_uri: String,
}

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

    #[test]
    fn release_gate_status_serde_contract_is_stable() {
        let pass = serde_json::to_string(&ReleaseGateStatus::Pass).expect("serialize pass");
        let fail = serde_json::to_string(&ReleaseGateStatus::Fail).expect("serialize fail");
        assert_eq!(pass, "\"pass\"");
        assert_eq!(fail, "\"fail\"");

        let parsed_pass: ReleaseGateStatus =
            serde_json::from_str("\"pass\"").expect("deserialize pass");
        let parsed_fail: ReleaseGateStatus =
            serde_json::from_str("\"fail\"").expect("deserialize fail");
        assert_eq!(parsed_pass, ReleaseGateStatus::Pass);
        assert_eq!(parsed_fail, ReleaseGateStatus::Fail);
        assert!(serde_json::from_str::<ReleaseGateStatus>("\"PASS\"").is_err());
    }

    #[test]
    fn dependency_audit_status_serde_contract_is_exhaustive_and_stable() {
        let expected = [
            (DependencyAuditStatus::Passed, "passed"),
            (
                DependencyAuditStatus::VulnerabilitiesFound,
                "vulnerabilities_found",
            ),
            (DependencyAuditStatus::ToolMissing, "tool_missing"),
            (DependencyAuditStatus::Error, "error"),
            (
                DependencyAuditStatus::HostToolsDisabled,
                "host_tools_disabled",
            ),
        ];

        for (status, raw) in expected {
            let serialized = serde_json::to_string(&status).expect("serialize status");
            assert_eq!(serialized, format!("\"{raw}\""));
            let deserialized: DependencyAuditStatus =
                serde_json::from_str(&serialized).expect("deserialize status");
            assert_eq!(deserialized, status);
        }
    }
}
