use crate::data_model::rc::{AsResourceSet, IpResourceSet};
use crate::fetch::rsync::RsyncFetcher;
use crate::policy::Policy;
use crate::report::Warning;
use crate::storage::RocksStore;
use crate::sync::rrdp::Fetcher as HttpFetcher;
use crate::validation::manifest::PublicationPointSource;
use crate::validation::objects::ObjectsOutput;
use crate::validation::tree::{CaCertificateRef, CaInstanceHandle, PublicationPointRunner};
use crate::validation::tree_runner::Rpkiv1PublicationPointRunner;
use std::collections::HashMap;
use std::sync::Mutex;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RunOutput {
    pub publication_point_source: PublicationPointSource,
    pub publication_point_warnings: Vec<Warning>,
    pub objects: ObjectsOutput,
}

#[derive(Debug, thiserror::Error)]
pub enum RunError {
    #[error("publication point runner failed: {0}")]
    Runner(String),
}

/// Offline-friendly end-to-end execution for a single publication point.
///
/// This reuses the same fresh-first runtime path as the tree runner:
/// 1) repository sync (RRDP or rsync fallback) into the current repository view
/// 2) manifest processing into a validated publication point result
/// 3) signed object processing (ROA/ASPA) and latest-VCIR persistence
pub fn run_publication_point_once(
    store: &RocksStore,
    policy: &Policy,
    rrdp_notification_uri: Option<&str>,
    rsync_base_uri: &str,
    manifest_rsync_uri: &str,
    publication_point_rsync_uri: &str,
    http_fetcher: &dyn HttpFetcher,
    rsync_fetcher: &dyn RsyncFetcher,
    issuer_ca_der: &[u8],
    issuer_ca_rsync_uri: Option<&str>,
    issuer_effective_ip: Option<&IpResourceSet>,
    issuer_effective_as: Option<&AsResourceSet>,
    validation_time: time::OffsetDateTime,
) -> Result<RunOutput, RunError> {
    let handle = CaInstanceHandle {
        depth: 0,
        tal_id: "single-publication-point".to_string(),
        parent_manifest_rsync_uri: None,
        ca_certificate: CaCertificateRef::inline_der(issuer_ca_der.to_vec()),
        ca_certificate_rsync_uri: issuer_ca_rsync_uri.map(str::to_string),
        effective_ip_resources: issuer_effective_ip.cloned(),
        effective_as_resources: issuer_effective_as.cloned(),
        rsync_base_uri: rsync_base_uri.to_string(),
        manifest_rsync_uri: manifest_rsync_uri.to_string(),
        publication_point_rsync_uri: publication_point_rsync_uri.to_string(),
        rrdp_notification_uri: rrdp_notification_uri.map(str::to_string),
    };

    let runner = Rpkiv1PublicationPointRunner {
        store,
        policy,
        http_fetcher,
        rsync_fetcher,
        validation_time,
        timing: None,
        download_log: None,
        replay_archive_index: None,
        replay_delta_index: None,
        rrdp_dedup: false,
        rrdp_repo_cache: Mutex::new(HashMap::new()),
        rsync_dedup: false,
        rsync_repo_cache: Mutex::new(HashMap::new()),
        current_repo_index: None,
        repo_sync_runtime: None,
        parallel_phase2_config: None,
        parallel_roa_worker_pool: None,
        ccr_accumulator: None,
        persist_vcir: true,
        enable_roa_validation_cache: false,
        enable_child_certificate_validation_cache: false,
        publication_point_cache_observe_only: false,
        enable_publication_point_validation_cache: false,
    };

    let result = runner
        .run_publication_point(&handle)
        .map_err(RunError::Runner)?;

    Ok(RunOutput {
        publication_point_source: result.source,
        publication_point_warnings: result.warnings,
        objects: result.objects,
    })
}
