use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;

use serde::Deserialize;
use time::OffsetDateTime;

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct SequenceItemRaw {
    pub(super) schema_version: Option<u32>,
    pub(super) rp_id: String,
    pub(super) side: Option<String>,
    pub(super) seq: u32,
    pub(super) run_id: String,
    pub(super) sync_mode: Option<String>,
    pub(super) status: Option<String>,
    pub(super) start_time: Option<String>,
    pub(super) finish_time: Option<String>,
    pub(super) validation_time: Option<String>,
    pub(super) ccr_path: PathBuf,
    pub(super) cir_path: PathBuf,
    pub(super) ccr_sha256: Option<String>,
    pub(super) cir_sha256: Option<String>,
    pub(super) wall_ms: Option<u64>,
    pub(super) max_rss_kb: Option<u64>,
    pub(super) vrps: Option<u64>,
    pub(super) vaps: Option<u64>,
    pub(super) publication_points: Option<u64>,
    pub(super) cir_object_count: Option<u64>,
    pub(super) cir_reject_count: Option<u64>,
    pub(super) cir_trust_anchor_count: Option<u64>,
}

#[derive(Clone, Debug)]
pub(super) struct SequenceSample {
    pub(super) raw: SequenceItemRaw,
    pub(super) validation_time: OffsetDateTime,
    pub(super) objects: BTreeMap<String, String>,
    pub(super) object_hashes: BTreeSet<String>,
    pub(super) rejects: BTreeSet<String>,
    pub(super) vrps: BTreeSet<String>,
    pub(super) vaps: BTreeSet<String>,
}

#[derive(Clone, Debug)]
pub(super) struct SequenceMeta {
    pub(super) raw: SequenceItemRaw,
    pub(super) validation_time: OffsetDateTime,
    pub(super) ccr_path: PathBuf,
    pub(super) cir_path: PathBuf,
    pub(super) cir_object_count: Option<u64>,
    pub(super) cir_reject_count: Option<u64>,
    pub(super) cir_trust_anchor_count: Option<u64>,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub(super) enum Side {
    #[default]
    Left,
    Right,
}

impl Side {
    pub(super) fn as_str(self) -> &'static str {
        match self {
            Side::Left => "left",
            Side::Right => "right",
        }
    }
}

#[derive(Clone, Debug)]
pub(super) struct SandwichRecord {
    pub(super) classification: &'static str,
    pub(super) set_type: &'static str,
    pub(super) key: String,
    pub(super) source_side: Side,
    pub(super) source_start_seq: u32,
    pub(super) source_start_run_id: String,
    pub(super) source_end_seq: u32,
    pub(super) source_end_run_id: String,
    pub(super) peer_seq: u32,
    pub(super) peer_run_id: String,
    pub(super) source_value: Option<String>,
    pub(super) peer_value: Option<String>,
    pub(super) source_start_time: String,
    pub(super) peer_time: String,
    pub(super) source_end_time: String,
    pub(super) note: String,
}

#[derive(Clone, Debug, Default)]
pub(super) struct SandwichClassStats {
    pub(super) total: usize,
    pub(super) unique_keys: BTreeSet<String>,
    pub(super) samples: Vec<SandwichRecord>,
}

#[derive(Clone, Debug, Default)]
pub(super) struct SandwichHeatmapRow {
    pub(super) window: String,
    pub(super) source_side: Side,
    pub(super) source_start_seq: u32,
    pub(super) source_start_run_id: String,
    pub(super) source_end_seq: u32,
    pub(super) source_end_run_id: String,
    pub(super) peer_seq: u32,
    pub(super) peer_run_id: String,
    pub(super) source_start_time: String,
    pub(super) peer_time: String,
    pub(super) source_end_time: String,
    pub(super) total: usize,
    pub(super) class_counts: BTreeMap<&'static str, usize>,
}

#[derive(Clone, Debug, Default)]
pub(super) struct SandwichGroupStats {
    pub(super) group_value: String,
    pub(super) set_type: &'static str,
    pub(super) total: usize,
    pub(super) unique_keys: BTreeSet<String>,
    pub(super) class_counts: BTreeMap<&'static str, usize>,
    pub(super) sample_keys: Vec<String>,
}

#[derive(Clone, Debug, Default)]
pub(super) struct SandwichAnalysis {
    pub(super) total_occurrences: usize,
    pub(super) unique_keys: BTreeSet<String>,
    pub(super) by_set_type: BTreeMap<&'static str, usize>,
    pub(super) stats: BTreeMap<&'static str, SandwichClassStats>,
    pub(super) heatmap: BTreeMap<String, SandwichHeatmapRow>,
    pub(super) uri_prefix_groups: BTreeMap<String, SandwichGroupStats>,
    pub(super) asn_groups: BTreeMap<String, SandwichGroupStats>,
}

#[derive(Clone, Debug)]
pub(super) struct ChurnRecord {
    pub(super) side: Side,
    pub(super) rp_id: String,
    pub(super) from_seq: u32,
    pub(super) to_seq: u32,
    pub(super) from_run_id: String,
    pub(super) to_run_id: String,
    pub(super) set_type: &'static str,
    pub(super) from_count: usize,
    pub(super) to_count: usize,
    pub(super) added_count: usize,
    pub(super) removed_count: usize,
    pub(super) changed_abs: usize,
    pub(super) union_count: usize,
    pub(super) changed_ratio_from: f64,
    pub(super) changed_ratio_union: f64,
}

#[derive(Clone, Debug)]
pub(super) struct ChurnSummaryRecord {
    pub(super) side: Side,
    pub(super) set_type: &'static str,
    pub(super) max_changed_abs: usize,
    pub(super) avg_changed_abs: f64,
    pub(super) max_changed_ratio_from: f64,
    pub(super) avg_changed_ratio_from: f64,
    pub(super) max_changed_ratio_union: f64,
    pub(super) avg_changed_ratio_union: f64,
}

#[derive(Clone, Debug, Default)]
pub(super) struct IntraRpChurn {
    pub(super) left: Vec<ChurnRecord>,
    pub(super) right: Vec<ChurnRecord>,
    pub(super) summary: Vec<ChurnSummaryRecord>,
}
