use std::collections::{BTreeMap, BTreeSet};
#[cfg(test)]
use std::env;
use std::fs;
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
use std::thread;
use std::time::{SystemTime, UNIX_EPOCH};

use dioprism_assets::budget::{
    AssetBudget, AssetBudgetAuditConfig, AssetBudgetAuditReport, AssetBudgetBatchPayload,
    AssetBudgetIntegrationConfig, AssetBudgetViolation, AssetHashStrategy, AssetKind,
    AssetLoadingPolicy, BudgetThreshold, BudgetViolationLevel, ContentHashAlgorithm,
    PreloadPreference, RouteAssetCost, RouteCost, SourceMapInclusion, aggregate_route_costs,
    apply_audit_config, budget_violations, content_hash_stream, content_hash_version,
    route_budget_violations, sort_asset_budgets, sort_budget_violations,
};
use dioprism_assets::css::{CssMinifyOptions, minify_css};
use flate2::Compression;
use flate2::write::GzEncoder;

pub fn run_from<I, S>(args: I) -> Result<(), String>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    run(args)
}

pub fn run_assets_from<I, S>(args: I) -> Result<(), String>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    run_from(args)
}

pub type AssetsCliIntegrationConfig = AssetBudgetIntegrationConfig;

pub const ASSETS_CLI_INTEGRATION_RANK_COVERAGE: &[u8] = &[
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
    27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
];

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AssetsCliCommandAdapter {
    pub command: String,
    pub normalized_args: Vec<String>,
    pub cache_key: String,
    pub labels: BTreeMap<String, String>,
}

impl AssetsCliCommandAdapter {
    pub fn new<I, S>(command: impl Into<String>, args: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        let command = command.into();
        let mut normalized_args = args.into_iter().map(Into::into).collect::<Vec<_>>();
        normalized_args.sort();
        let descriptor = format!(
            "dioprism-assets-cli:adapter:v1\ncommand={command}\nargs={}\n",
            normalized_args.join("\n")
        );
        Self {
            command,
            normalized_args,
            cache_key: content_hash_version(descriptor.as_bytes()),
            labels: BTreeMap::new(),
        }
    }

    pub fn for_command<I, S>(command: impl Into<String>, args: I) -> Self
    where
        I: IntoIterator<Item = S>,
        S: Into<String>,
    {
        Self::new(command, args)
    }

    pub fn label(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.labels.insert(key.into(), value.into());
        self
    }

    pub fn explain(&self) -> String {
        format!(
            "assets-cli command={} args={} cacheKey={}",
            self.command,
            self.normalized_args.len(),
            self.cache_key
        )
    }
}

pub fn assets_cli_command<I, S>(command: impl Into<String>, args: I) -> AssetsCliCommandAdapter
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    AssetsCliCommandAdapter::for_command(command, args)
}

pub fn assets_cli_config() -> AssetsCliIntegrationConfig {
    AssetBudgetIntegrationConfig::new()
        .tag("dioprism-assets-cli")
        .label("surface", "cli")
}

pub fn assets_cli_batch_payload(report: &AssetBudgetAuditReport) -> AssetBudgetBatchPayload {
    AssetBudgetBatchPayload::from_assets(report.assets.clone())
}

fn run<I, S>(args: I) -> Result<(), String>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    let mut args = args.into_iter().map(Into::into);
    let Some(command) = args.next() else {
        return Err(help());
    };

    match command.as_str() {
        "css-minify" | "css" => css_minify(args.collect()),
        "hash" => hash_asset(args.collect()),
        "precompress" | "zip" => precompress(args.collect()),
        "budget-audit" | "budget" => budget_audit(args.collect()),
        "route-plan" | "routes" => route_plan(args.collect()),
        "--help" | "-h" | "help" => {
            println!("{}", help());
            Ok(())
        }
        other => Err(format!("Unknown command `{other}`.\n{}", help())),
    }
}

#[derive(Debug)]
struct RoutePlanOptions {
    input: PathBuf,
    output: PathBuf,
    budget_bytes: Option<usize>,
    duplicate_detection: DuplicateAssetDetection,
}

#[derive(Debug)]
struct RoutePlanEntry {
    route: String,
    source: PathBuf,
    runtime_content_hash: String,
    bytes: usize,
    assets: Vec<String>,
    packages: Vec<String>,
    render_lanes: Vec<String>,
    critical_runtime: Vec<String>,
    idle_runtime: Vec<String>,
    critical_css: Vec<String>,
    visual_profile: String,
    lcp_protection: String,
    package_cost: String,
    speculation: serde_json::Value,
    compression_candidates: Vec<String>,
    dictionary_candidates: Vec<String>,
    asset_budgets: Vec<AssetBudget>,
    route_asset_costs: Vec<RouteAssetCost>,
    budget_violations: Vec<AssetBudgetViolation>,
}

#[derive(Debug, Clone)]
struct RouteRuntimeSource {
    source: PathBuf,
    runtime: serde_json::Value,
    content_hash: String,
}

#[cfg(test)]
#[derive(Debug, Default)]
struct RouteRuntimeSourceCache {
    parsed: BTreeMap<PathBuf, Option<RouteRuntimeSource>>,
}

#[derive(Debug, Default)]
struct RoutePackageCostCache {
    costs: BTreeMap<String, &'static str>,
}

impl RoutePackageCostCache {
    fn route_package_cost(
        &mut self,
        packages: &[String],
        render_lanes: &BTreeSet<String>,
    ) -> &'static str {
        let key = route_package_cost_cache_key(packages, render_lanes);
        if let Some(cost) = self.costs.get(&key) {
            return cost;
        }
        let cost = route_package_cost(packages, render_lanes);
        self.costs.insert(key, cost);
        cost
    }
}

#[cfg(test)]
impl RouteRuntimeSourceCache {
    fn load(&mut self, source: &Path) -> Result<Option<RouteRuntimeSource>, String> {
        if let Some(cached) = self.parsed.get(source) {
            return Ok(cached.clone());
        }

        let cached = load_route_runtime_source(source)?;
        self.parsed.insert(source.to_path_buf(), cached.clone());
        Ok(cached)
    }

    #[cfg(test)]
    fn len(&self) -> usize {
        self.parsed.len()
    }
}

fn load_route_runtime_source(source: &Path) -> Result<Option<RouteRuntimeSource>, String> {
    let text = fs::read_to_string(source)
        .map_err(|error| format!("Failed to read {}: {error}", source.display()))?;
    let content_hash = content_hash_version(text.as_bytes());
    let runtime = extract_route_runtime_json(&text, source)?;
    Ok(runtime.map(|runtime| RouteRuntimeSource {
        source: source.to_path_buf(),
        runtime,
        content_hash,
    }))
}

#[derive(Debug, Default)]
struct FileContentHashCache {
    hashes: BTreeMap<PathBuf, String>,
}

impl FileContentHashCache {
    fn hash_file(&mut self, path: &Path, strategy: AssetHashStrategy) -> Result<String, String> {
        if let Some(hash) = self.hashes.get(path) {
            return Ok(hash.clone());
        }
        let file = fs::File::open(path)
            .map_err(|error| format!("Failed to read {}: {error}", path.display()))?;
        let hash = content_hash_stream(file, strategy)
            .map_err(|error| format!("Failed to hash {}: {error}", path.display()))?;
        self.hashes.insert(path.to_path_buf(), hash.clone());
        Ok(hash)
    }
}

#[derive(Debug)]
struct BudgetAuditOptions {
    input: PathBuf,
    output: Option<PathBuf>,
    format: BudgetAuditFormat,
    budget_bytes: Option<u64>,
    route_budget_bytes: Option<u64>,
    baseline: Option<PathBuf>,
    source_map_inclusion: SourceMapInclusion,
    compact_json: bool,
    fail_on: BudgetAuditFailOn,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BudgetAuditFormat {
    Text,
    Json,
    JsonLines,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BudgetAuditFailOn {
    Never,
    Error,
    Warning,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DuplicateAssetDetection {
    Off,
    Path,
    ContentHash,
}

fn budget_audit(args: Vec<String>) -> Result<(), String> {
    let mut input: Option<PathBuf> = None;
    let mut output: Option<PathBuf> = None;
    let mut format = BudgetAuditFormat::Text;
    let mut budget_bytes: Option<u64> = None;
    let mut route_budget_bytes: Option<u64> = None;
    let mut baseline: Option<PathBuf> = None;
    let mut source_map_inclusion = SourceMapInclusion::Exclude;
    let mut compact_json = false;
    let mut fail_on = BudgetAuditFailOn::Error;
    let mut index = 0usize;
    while index < args.len() {
        match args[index].as_str() {
            "--input" => {
                index += 1;
                input = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--input requires a path".to_string())?,
                ));
            }
            "--output" => {
                index += 1;
                output = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--output requires a path".to_string())?,
                ));
            }
            "--format" => {
                index += 1;
                format = match args
                    .get(index)
                    .ok_or_else(|| "--format requires text or json".to_string())?
                    .as_str()
                {
                    "text" => BudgetAuditFormat::Text,
                    "json" => BudgetAuditFormat::Json,
                    "json-lines" | "jsonl" => BudgetAuditFormat::JsonLines,
                    other => {
                        return Err(format!(
                            "Unknown budget-audit format `{other}`. Expected text, json, or json-lines."
                        ));
                    }
                };
            }
            "--budget-bytes" => {
                index += 1;
                budget_bytes = Some(parse_u64_arg(&args, index, "--budget-bytes")?);
            }
            "--route-budget-bytes" => {
                index += 1;
                route_budget_bytes = Some(parse_u64_arg(&args, index, "--route-budget-bytes")?);
            }
            "--baseline" => {
                index += 1;
                baseline = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--baseline requires a path".to_string())?,
                ));
            }
            "--include-source-maps" => source_map_inclusion = SourceMapInclusion::Include,
            "--separate-source-map-budget" => {
                source_map_inclusion = SourceMapInclusion::SeparateBudget;
            }
            "--compact-json" => compact_json = true,
            "--fail-on" => {
                index += 1;
                fail_on =
                    parse_budget_fail_on(args.get(index).ok_or_else(|| {
                        "--fail-on requires warning, error, or never".to_string()
                    })?)?;
            }
            "--fail-on-warning" => fail_on = BudgetAuditFailOn::Warning,
            "--allow-violations" | "--no-fail" => fail_on = BudgetAuditFailOn::Never,
            "--help" | "-h" => {
                println!("{}", help_budget_audit());
                return Ok(());
            }
            other => {
                return Err(format!(
                    "Unknown budget-audit option `{other}`.\n{}",
                    help_budget_audit()
                ));
            }
        }
        index += 1;
    }

    let options = BudgetAuditOptions {
        input: input.ok_or_else(|| "--input is required".to_string())?,
        output,
        format,
        budget_bytes,
        route_budget_bytes,
        baseline,
        source_map_inclusion,
        compact_json,
        fail_on,
    };
    run_budget_audit(options)
}

fn run_budget_audit(options: BudgetAuditOptions) -> Result<(), String> {
    let report = budget_audit_report(
        &options.input,
        options.budget_bytes,
        options.route_budget_bytes,
        options.source_map_inclusion,
        options.baseline.as_deref(),
    )?;
    let rendered = match options.format {
        BudgetAuditFormat::Text => format_budget_audit_text(&report),
        BudgetAuditFormat::Json => if options.compact_json {
            serde_json::to_string(&report)
        } else {
            serde_json::to_string_pretty(&report)
        }
        .map_err(|error| format!("Failed to serialize budget audit: {error}"))?,
        BudgetAuditFormat::JsonLines => format_budget_audit_json_lines(&report)?,
    };

    if let Some(output) = options.output {
        write_output(&output, rendered.as_bytes())
            .map_err(|error| format!("Failed to write {}: {error}", output.display()))?;
        println!("Asset budget audit: {}", output.display());
    } else {
        println!("{rendered}");
    }

    if budget_audit_failed(&report, options.fail_on) {
        return Err(format!(
            "Asset budget audit failed with {} violation(s).",
            report.violations.len()
        ));
    }
    Ok(())
}

fn budget_audit_report(
    input: &Path,
    budget_bytes: Option<u64>,
    route_budget_bytes: Option<u64>,
    source_map_inclusion: SourceMapInclusion,
    baseline: Option<&Path>,
) -> Result<AssetBudgetAuditReport, String> {
    let mut config = AssetBudgetAuditConfig::new();
    config.source_map_inclusion = source_map_inclusion;
    if let Some(budget_bytes) = budget_bytes {
        config.default_budget = Some(BudgetThreshold::error(budget_bytes));
    }
    let assets = apply_audit_config(collect_asset_budgets(input, None)?, &config);
    let mut report = AssetBudgetAuditReport::from_assets(assets);
    if let Some(route_budget_bytes) = route_budget_bytes {
        report
            .violations
            .extend(route_budget_violations(&report.routes, route_budget_bytes));
        sort_budget_violations(&mut report.violations);
    }
    if let Some(baseline) = baseline {
        report
            .violations
            .extend(baseline_regressions(&report, baseline)?);
        sort_budget_violations(&mut report.violations);
    }
    Ok(report)
}

fn budget_audit_failed(report: &AssetBudgetAuditReport, fail_on: BudgetAuditFailOn) -> bool {
    match fail_on {
        BudgetAuditFailOn::Never => false,
        BudgetAuditFailOn::Error => report.has_error_violations(),
        BudgetAuditFailOn::Warning => {
            report.has_error_violations() || report.has_warning_violations()
        }
    }
}

fn format_budget_audit_text(report: &AssetBudgetAuditReport) -> String {
    let mut lines = Vec::new();
    lines.push(format!(
        "asset-budget-audit assets={} routes={} violations={}",
        report.asset_count,
        report.route_count,
        report.violations.len()
    ));
    for route in &report.routes {
        lines.push(format!(
            "ROUTE_COST route={} totalBytes={} measuredBytes={} estimatedBytes={} assetCount={} packageCount={} costUnits={} criticalAssets={}",
            route.route_id,
            route.total_bytes,
            route.measured_bytes,
            route.estimated_bytes,
            route.asset_count,
            route.package_count,
            route.cost_units,
            route.critical_asset_count
        ));
    }
    for violation in &report.violations {
        lines.push(format!(
            "BUDGET_VIOLATION level={} route={} package={} asset={} measuredBytes={} thresholdBytes={}",
            budget_violation_level_label(violation.level),
            violation.route_id.as_deref().unwrap_or("global"),
            violation.package_id,
            violation.asset_id,
            violation.measured_bytes,
            violation.threshold_bytes
        ));
    }
    if report.violations.is_empty() {
        lines.push("BUDGET_OK".to_string());
    }
    lines.join("\n")
}

fn format_budget_audit_json_lines(report: &AssetBudgetAuditReport) -> Result<String, String> {
    let mut lines = Vec::new();
    for route in &report.routes {
        lines.push(
            serde_json::to_string(&serde_json::json!({
                "type": "routeCost",
                "route": route,
            }))
            .map_err(|error| format!("Failed to serialize route json-line: {error}"))?,
        );
    }
    for violation in &report.violations {
        lines.push(
            serde_json::to_string(&serde_json::json!({
                "type": "budgetViolation",
                "violation": violation,
            }))
            .map_err(|error| format!("Failed to serialize violation json-line: {error}"))?,
        );
    }
    if lines.is_empty() {
        lines.push(r#"{"type":"budgetOk"}"#.to_string());
    }
    Ok(lines.join("\n"))
}

fn budget_violation_level_label(level: BudgetViolationLevel) -> &'static str {
    match level {
        BudgetViolationLevel::Warning => "warning",
        BudgetViolationLevel::Error => "error",
    }
}

fn parse_budget_fail_on(value: &str) -> Result<BudgetAuditFailOn, String> {
    match value {
        "never" | "none" => Ok(BudgetAuditFailOn::Never),
        "error" | "errors" => Ok(BudgetAuditFailOn::Error),
        "warning" | "warnings" => Ok(BudgetAuditFailOn::Warning),
        other => Err(format!(
            "Unknown --fail-on value `{other}`. Expected warning, error, or never."
        )),
    }
}

fn parse_u64_arg(args: &[String], index: usize, flag: &str) -> Result<u64, String> {
    let value = args
        .get(index)
        .ok_or_else(|| format!("{flag} requires a number"))?;
    value
        .parse::<u64>()
        .map_err(|_| format!("{flag} must be a positive integer"))
}

fn baseline_regressions(
    report: &AssetBudgetAuditReport,
    baseline_path: &Path,
) -> Result<Vec<AssetBudgetViolation>, String> {
    let baseline = fs::read_to_string(baseline_path).map_err(|error| {
        format!(
            "Failed to read baseline {}: {error}",
            baseline_path.display()
        )
    })?;
    let baseline: BTreeMap<String, u64> = serde_json::from_str(&baseline).map_err(|error| {
        format!(
            "Failed to parse baseline {}: {error}",
            baseline_path.display()
        )
    })?;
    let mut violations = Vec::new();
    for route in &report.routes {
        let Some(previous_bytes) = baseline.get(&route.route_id) else {
            continue;
        };
        if route.total_bytes > *previous_bytes {
            violations.push(AssetBudgetViolation {
                level: BudgetViolationLevel::Warning,
                asset_id: "__route_regression__".to_string(),
                package_id: "__route__".to_string(),
                route_id: Some(route.route_id.clone()),
                measured_bytes: route.total_bytes,
                threshold_bytes: *previous_bytes,
                message: format!(
                    "route `{}` regressed from {} bytes to {} bytes",
                    route.route_id, previous_bytes, route.total_bytes
                ),
            });
        }
    }
    sort_budget_violations(&mut violations);
    Ok(violations)
}

fn route_plan(args: Vec<String>) -> Result<(), String> {
    let mut input: Option<PathBuf> = None;
    let mut output: Option<PathBuf> = None;
    let mut budget_bytes: Option<usize> = None;
    let mut duplicate_detection = DuplicateAssetDetection::Path;
    let mut index = 0usize;
    while index < args.len() {
        match args[index].as_str() {
            "--input" => {
                index += 1;
                input = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--input requires a path".to_string())?,
                ));
            }
            "--output" => {
                index += 1;
                output = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--output requires a path".to_string())?,
                ));
            }
            "--budget-bytes" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--budget-bytes requires a number".to_string())?;
                budget_bytes = Some(
                    value
                        .parse::<usize>()
                        .map_err(|_| "--budget-bytes must be a positive integer".to_string())?,
                );
            }
            "--duplicate-detection" => {
                index += 1;
                duplicate_detection =
                    parse_duplicate_asset_detection(args.get(index).ok_or_else(|| {
                        "--duplicate-detection requires off, path, or content-hash".to_string()
                    })?)?;
            }
            "--help" | "-h" => {
                println!("{}", help_route_plan());
                return Ok(());
            }
            other => {
                return Err(format!(
                    "Unknown route-plan option `{other}`.\n{}",
                    help_route_plan()
                ));
            }
        }
        index += 1;
    }
    let options = RoutePlanOptions {
        input: input.ok_or_else(|| "--input is required".to_string())?,
        output: output.ok_or_else(|| "--output is required".to_string())?,
        budget_bytes,
        duplicate_detection,
    };
    run_route_plan(options)
}

fn parse_duplicate_asset_detection(value: &str) -> Result<DuplicateAssetDetection, String> {
    match value {
        "off" | "none" => Ok(DuplicateAssetDetection::Off),
        "path" => Ok(DuplicateAssetDetection::Path),
        "content-hash" | "content" | "hash" => Ok(DuplicateAssetDetection::ContentHash),
        other => Err(format!(
            "Unknown duplicate detection `{other}`. Expected off, path, or content-hash."
        )),
    }
}

fn run_route_plan(options: RoutePlanOptions) -> Result<(), String> {
    let sources = collect_route_runtime_sources(&options.input)?;
    let mut entries = Vec::new();
    let mut all_asset_budgets = Vec::new();
    let mut asset_paths = Vec::new();
    let mut package_cost_cache = RoutePackageCostCache::default();
    for source in sources {
        let entry = route_plan_entry(&source, &mut package_cost_cache)?;
        for asset in &entry.assets {
            asset_paths.push(asset.clone());
        }
        all_asset_budgets.extend(entry.asset_budgets.iter().cloned());
        entries.push(entry);
    }
    sort_asset_budgets(&mut all_asset_budgets);

    let duplicate_assets = duplicate_asset_report(
        &asset_paths,
        &all_asset_budgets,
        options.duplicate_detection,
    );
    let route_values = entries
        .iter()
        .map(|entry| {
            serde_json::json!({
                "route": entry.route,
                "source": entry.source.display().to_string(),
                "runtimeContentHash": entry.runtime_content_hash,
                "runtimeBytes": entry.bytes,
                "overBudget": options.budget_bytes.is_some_and(|budget| entry.bytes > budget),
                "assets": entry.assets,
                "packages": entry.packages,
                "renderLanes": entry.render_lanes,
                "criticalRuntime": entry.critical_runtime,
                "idleRuntime": entry.idle_runtime,
                "criticalCss": entry.critical_css,
                "visualProfile": entry.visual_profile,
                "lcpProtection": entry.lcp_protection,
                "packageCost": entry.package_cost,
                "speculation": entry.speculation,
                "preloadManifest": preload_manifest_value(entry),
                "compressionCandidates": entry.compression_candidates,
                "dictionaryCandidates": entry.dictionary_candidates,
                "assetBudgets": entry.asset_budgets,
                "routeAssetCosts": entry.route_asset_costs,
                "budgetViolations": entry.budget_violations,
            })
        })
        .collect::<Vec<_>>();
    let all_route_costs = aggregate_route_costs(&all_asset_budgets);
    let all_budget_violations = budget_violations(&all_asset_budgets);
    let report = serde_json::json!({
        "version": 1,
        "kind": "dioxus-route-asset-plan",
        "budgetBytes": options.budget_bytes,
        "routeCount": entries.len(),
        "duplicateAssets": duplicate_assets,
        "assetBudgets": all_asset_budgets,
        "routeAssetCosts": all_route_costs,
        "budgetViolations": all_budget_violations,
        "routes": route_values,
    });
    let bytes = serde_json::to_vec_pretty(&report)
        .map_err(|error| format!("Failed to serialize route plan: {error}"))?;
    write_output(&options.output, &bytes)
        .map_err(|error| format!("Failed to write {}: {error}", options.output.display()))?;
    println!("Route asset plan: {}", options.output.display());
    Ok(())
}

fn preload_manifest_value(entry: &RoutePlanEntry) -> serde_json::Value {
    serde_json::json!({
        "critical": entry.critical_runtime,
        "idle": entry.idle_runtime,
        "assets": entry.assets,
    })
}

fn duplicate_asset_report(
    asset_paths: &[String],
    asset_budgets: &[AssetBudget],
    detection: DuplicateAssetDetection,
) -> Vec<serde_json::Value> {
    if detection == DuplicateAssetDetection::Off {
        return Vec::new();
    }

    let mut occurrences: BTreeMap<String, usize> = BTreeMap::new();
    match detection {
        DuplicateAssetDetection::Off => {}
        DuplicateAssetDetection::Path => {
            for path in asset_paths {
                *occurrences.entry(path.clone()).or_default() += 1;
            }
        }
        DuplicateAssetDetection::ContentHash => {
            for budget in asset_budgets {
                let key = if budget.cache_version.is_empty() {
                    budget.asset_id.clone()
                } else {
                    budget.cache_version.clone()
                };
                *occurrences.entry(key).or_default() += 1;
            }
        }
    }

    occurrences
        .iter()
        .filter(|&(_key, count)| *count > 1)
        .map(|(key, count)| {
            let key_name = if detection == DuplicateAssetDetection::ContentHash {
                "contentHash"
            } else {
                "path"
            };
            let mut value = serde_json::Map::new();
            value.insert(key_name.to_string(), serde_json::Value::String(key.clone()));
            value.insert(
                "count".to_string(),
                serde_json::Value::Number(serde_json::Number::from(*count as u64)),
            );
            serde_json::Value::Object(value)
        })
        .collect()
}

fn route_plan_entry(
    source: &RouteRuntimeSource,
    package_cost_cache: &mut RoutePackageCostCache,
) -> Result<RoutePlanEntry, String> {
    let runtime = &source.runtime;
    let route = runtime
        .get("route")
        .or_else(|| runtime.get("r"))
        .and_then(serde_json::Value::as_str)
        .unwrap_or_else(|| {
            source
                .source
                .file_stem()
                .and_then(|value| value.to_str())
                .unwrap_or("unknown")
        })
        .to_string();
    let mut assets = Vec::new();
    for asset in runtime
        .get("assets")
        .or_else(|| runtime.get("a"))
        .and_then(serde_json::Value::as_object)
        .into_iter()
        .flat_map(|assets| assets.values())
    {
        if let Some(path) = asset.get("path").and_then(serde_json::Value::as_str) {
            assets.push(path.to_string());
        }
    }
    let packages = runtime
        .get("packages")
        .or_else(|| runtime.get("pkg"))
        .and_then(serde_json::Value::as_object)
        .map(|packages| packages.keys().cloned().collect::<Vec<_>>())
        .unwrap_or_default();
    let mut render_lanes = BTreeSet::new();
    if let Some(packages) = runtime
        .get("packages")
        .or_else(|| runtime.get("pkg"))
        .and_then(serde_json::Value::as_object)
    {
        for package in packages.values() {
            if let Some(lanes) = package
                .get("renderLanes")
                .and_then(serde_json::Value::as_array)
            {
                for lane in lanes {
                    if let Some(lane) = lane.as_str() {
                        render_lanes.insert(lane.to_string());
                    }
                }
            }
        }
    }
    let route_policy = runtime
        .get("policies")
        .or_else(|| runtime.get("p"))
        .and_then(|policies| policies.get("route"));
    let critical_runtime = route_policy
        .and_then(|policy| policy.get("criticalRuntime"))
        .and_then(serde_json::Value::as_array)
        .map(|values| json_string_array(values))
        .unwrap_or_default();
    let idle_runtime = route_policy
        .and_then(|policy| policy.get("idleRuntime"))
        .and_then(serde_json::Value::as_array)
        .map(|values| json_string_array(values))
        .unwrap_or_default();
    let critical_css = route_policy
        .and_then(|policy| policy.get("criticalCss"))
        .and_then(serde_json::Value::as_array)
        .map(|values| json_string_array(values))
        .unwrap_or_default();
    let visual_profile = route_policy
        .and_then(|policy| policy.get("visualProfile"))
        .and_then(serde_json::Value::as_str)
        .unwrap_or("static-first")
        .to_string();
    let lcp_protection = route_policy
        .and_then(|policy| policy.get("lcpProtection"))
        .and_then(serde_json::Value::as_str)
        .unwrap_or("unknown")
        .to_string();
    let speculation = route_policy
        .and_then(|policy| policy.get("speculation"))
        .cloned()
        .unwrap_or_else(|| serde_json::json!({}));
    let package_cost = package_cost_cache
        .route_package_cost(&packages, &render_lanes)
        .to_string();
    let compression_candidates = route_compression_candidates(
        assets
            .iter()
            .chain(critical_runtime.iter())
            .chain(idle_runtime.iter()),
    );
    let dictionary_candidates = route_dictionary_candidates(&compression_candidates);
    let asset_budgets = route_runtime_asset_budgets(&source.source, runtime, None)?;
    let route_asset_costs = aggregate_route_costs(&asset_budgets);
    let budget_violations = budget_violations(&asset_budgets);
    Ok(RoutePlanEntry {
        route,
        source: source.source.clone(),
        runtime_content_hash: source.content_hash.clone(),
        bytes: serde_json::to_vec(runtime)
            .map_err(|error| format!("Failed to size route runtime: {error}"))?
            .len(),
        assets,
        packages,
        render_lanes: render_lanes.into_iter().collect(),
        critical_runtime,
        idle_runtime,
        critical_css,
        visual_profile,
        lcp_protection,
        package_cost,
        speculation,
        compression_candidates,
        dictionary_candidates,
        asset_budgets,
        route_asset_costs,
        budget_violations,
    })
}

fn collect_asset_budgets(
    input: &Path,
    default_budget_bytes: Option<u64>,
) -> Result<Vec<AssetBudget>, String> {
    let sources = collect_route_runtime_sources(input)?;
    let mut budgets = Vec::new();
    for source in sources {
        budgets.extend(route_runtime_asset_budgets(
            &source.source,
            &source.runtime,
            default_budget_bytes,
        )?);
    }

    if budgets.is_empty() {
        return Err("No asset budgets or route runtime assets found".to_string());
    }
    sort_asset_budgets(&mut budgets);
    Ok(budgets)
}

fn route_runtime_asset_budgets(
    source: &Path,
    runtime: &serde_json::Value,
    default_budget_bytes: Option<u64>,
) -> Result<Vec<AssetBudget>, String> {
    let route = route_runtime_route(source, runtime);
    let mut budgets = explicit_asset_budgets(runtime, &route, default_budget_bytes)?;
    let explicit_ids = budgets
        .iter()
        .map(|budget| budget.asset_id.clone())
        .collect::<BTreeSet<_>>();
    budgets.extend(
        fallback_runtime_asset_budgets(runtime, &route, default_budget_bytes)?
            .into_iter()
            .filter(|budget| !explicit_ids.contains(&budget.asset_id)),
    );
    sort_asset_budgets(&mut budgets);
    Ok(budgets)
}

fn explicit_asset_budgets(
    runtime: &serde_json::Value,
    route: &str,
    default_budget_bytes: Option<u64>,
) -> Result<Vec<AssetBudget>, String> {
    let mut values = Vec::new();
    collect_explicit_budget_values(runtime, &mut values);

    if let Some(packages) = runtime_packages_object(runtime) {
        for package in packages.values() {
            collect_explicit_budget_values(package, &mut values);
        }
    }

    let mut budgets = Vec::new();
    for value in values {
        let mut budget: AssetBudget = serde_json::from_value(value.clone())
            .map_err(|error| format!("Failed to parse asset budget metadata `{value}`: {error}"))?;
        normalize_budget(&mut budget, route, default_budget_bytes);
        budgets.push(budget);
    }
    Ok(budgets)
}

fn collect_explicit_budget_values(value: &serde_json::Value, values: &mut Vec<serde_json::Value>) {
    for key in ["assetBudgets", "asset_budgets", "ab"] {
        if let Some(next) = value.get(key) {
            push_budget_values(next, values);
        }
    }
    if let Some(budgets) = value.get("budgets") {
        for key in ["assets", "assetBudgets", "asset_budgets"] {
            if let Some(next) = budgets.get(key) {
                push_budget_values(next, values);
            }
        }
    }
    if let Some(assets) = value.get("assets").and_then(serde_json::Value::as_array) {
        for asset in assets {
            if asset.get("assetId").is_some() || asset.get("asset_id").is_some() {
                values.push(asset.clone());
            }
        }
    }
}

fn push_budget_values(value: &serde_json::Value, values: &mut Vec<serde_json::Value>) {
    if let Some(array) = value.as_array() {
        values.extend(array.iter().cloned());
        return;
    }
    if value.get("assetId").is_some() || value.get("asset_id").is_some() {
        values.push(value.clone());
        return;
    }
    if let Some(object) = value.as_object() {
        values.extend(
            object
                .values()
                .filter(|value| value.get("assetId").is_some() || value.get("asset_id").is_some())
                .cloned(),
        );
    }
}

fn fallback_runtime_asset_budgets(
    runtime: &serde_json::Value,
    route: &str,
    default_budget_bytes: Option<u64>,
) -> Result<Vec<AssetBudget>, String> {
    let mut budgets = Vec::new();
    let Some(assets) = runtime_assets_object(runtime) else {
        return Ok(budgets);
    };
    let packages = runtime_packages_object(runtime);
    let package_ids = packages
        .map(|packages| packages.keys().cloned().collect::<Vec<_>>())
        .unwrap_or_default();

    for (asset_id, asset) in assets {
        let path = runtime_asset_path(asset).unwrap_or(asset_id.as_str());
        let package_id = runtime_asset_package_id(asset)
            .unwrap_or_else(|| infer_package_id(asset_id, &package_ids));
        let mut budget = AssetBudget::new(
            asset_id.clone(),
            package_id.clone(),
            runtime_asset_kind(asset, path),
        );
        if let Some(bytes) = json_u64(asset, &["estimatedBytes", "estimated", "size"]) {
            budget = budget.with_estimated_bytes(bytes);
        }
        if let Some(bytes) = json_u64(asset, &["measuredBytes", "measured", "bytes", "b"]) {
            budget = budget.with_measured_bytes(bytes);
        }
        let preload = runtime_preload_label(asset)
            .or_else(|| package_preload_label(packages, &package_id))
            .unwrap_or("none");
        budget = budget
            .with_loading_policy(loading_policy_from_preload(preload))
            .with_preload_preference(preload_preference_from_label(preload))
            .with_route_cost(runtime_route_cost(asset, packages, &package_id, route));
        if let Some(version) = runtime_asset_version(asset).or_else(|| query_version(path)) {
            budget = budget.with_cache_version(version);
        }
        if let Some(cache_key) = json_string(asset, &["cacheKey", "cache_key"]) {
            let cache_version = budget.cache_version.clone();
            budget = budget.with_cache(cache_key, cache_version);
        }
        if budget.budget_threshold.is_empty()
            && let Some(threshold) = asset
                .get("budgetThreshold")
                .or_else(|| asset.get("budget_threshold"))
                .or_else(|| asset.get("budget"))
        {
            budget = budget.with_budget_threshold(
                serde_json::from_value(threshold.clone()).unwrap_or_default(),
            );
        }
        normalize_budget(&mut budget, route, default_budget_bytes);
        budgets.push(budget);
    }
    Ok(budgets)
}

fn normalize_budget(budget: &mut AssetBudget, route: &str, default_budget_bytes: Option<u64>) {
    if budget.route_cost.route_id.is_none() {
        budget.route_cost.route_id = Some(route.to_string());
    }
    if budget.route_cost.cost_units == 0 {
        budget.route_cost.cost_units = 1;
    }
    if budget.budget_threshold.is_empty()
        && let Some(default_budget_bytes) = default_budget_bytes
    {
        budget.budget_threshold = BudgetThreshold::error(default_budget_bytes);
    }
    if budget.cache_key.is_empty() {
        budget.cache_key = budget.derived_cache_key();
    }
}

fn runtime_assets_object(
    runtime: &serde_json::Value,
) -> Option<&serde_json::Map<String, serde_json::Value>> {
    runtime
        .get("assets")
        .or_else(|| runtime.get("a"))
        .and_then(serde_json::Value::as_object)
}

fn runtime_packages_object(
    runtime: &serde_json::Value,
) -> Option<&serde_json::Map<String, serde_json::Value>> {
    runtime
        .get("packages")
        .or_else(|| runtime.get("pkg"))
        .and_then(serde_json::Value::as_object)
}

fn route_runtime_route(source: &Path, runtime: &serde_json::Value) -> String {
    runtime
        .get("route")
        .or_else(|| runtime.get("r"))
        .and_then(serde_json::Value::as_str)
        .unwrap_or_else(|| {
            source
                .file_stem()
                .and_then(|value| value.to_str())
                .unwrap_or("unknown")
        })
        .to_string()
}

fn runtime_asset_path(asset: &serde_json::Value) -> Option<&str> {
    json_string(asset, &["path", "module", "href", "p", "m"])
}

fn runtime_asset_version(asset: &serde_json::Value) -> Option<&str> {
    json_string(asset, &["version", "cacheVersion", "cache_version", "v"])
}

fn runtime_asset_package_id(asset: &serde_json::Value) -> Option<String> {
    json_string(asset, &["packageId", "package_id", "package", "pkg"]).map(ToString::to_string)
}

fn runtime_asset_kind(asset: &serde_json::Value, path: &str) -> AssetKind {
    json_string(asset, &["assetKind", "asset_kind", "kind", "k"])
        .map(AssetKind::from_label)
        .filter(|kind| *kind != AssetKind::Other)
        .unwrap_or_else(|| {
            if path.contains("worker") {
                AssetKind::Worker
            } else {
                AssetKind::from_path(path)
            }
        })
}

fn runtime_preload_label(asset: &serde_json::Value) -> Option<&str> {
    json_string(
        asset,
        &[
            "preloadPreference",
            "preload_preference",
            "preloadPolicy",
            "preload_policy",
            "preload",
            "l",
        ],
    )
}

fn package_preload_label<'a>(
    packages: Option<&'a serde_json::Map<String, serde_json::Value>>,
    package_id: &str,
) -> Option<&'a str> {
    packages
        .and_then(|packages| packages.get(package_id))
        .and_then(|package| json_string(package, &["preloadPolicy", "preload_policy", "l"]))
}

fn loading_policy_from_preload(preload: &str) -> AssetLoadingPolicy {
    match preload {
        "eager" | "preload" | "module-preload" | "early-hints" => AssetLoadingPolicy::Eager,
        _ => AssetLoadingPolicy::Lazy,
    }
}

fn preload_preference_from_label(preload: &str) -> PreloadPreference {
    match preload {
        "idle" => PreloadPreference::Idle,
        "visible" => PreloadPreference::Visible,
        "eager" | "preload" => PreloadPreference::Preload,
        "prefetch" => PreloadPreference::Prefetch,
        "module-preload" => PreloadPreference::ModulePreload,
        "early-hints" => PreloadPreference::EarlyHints,
        _ => PreloadPreference::None,
    }
}

fn runtime_route_cost(
    asset: &serde_json::Value,
    packages: Option<&serde_json::Map<String, serde_json::Value>>,
    package_id: &str,
    route: &str,
) -> RouteCost {
    if let Some(route_cost) = asset.get("routeCost").or_else(|| asset.get("route_cost"))
        && let Ok(mut route_cost) = serde_json::from_value::<RouteCost>(route_cost.clone())
    {
        if route_cost.route_id.is_none() {
            route_cost.route_id = Some(route.to_string());
        }
        return route_cost;
    }
    let package = packages.and_then(|packages| packages.get(package_id));
    let cost_units = package
        .and_then(|package| json_string(package, &["packageCost", "package_cost"]))
        .map(package_cost_units)
        .unwrap_or(1);
    let critical = package
        .and_then(|package| json_string(package, &["priority"]))
        .is_some_and(|priority| priority == "critical")
        || runtime_preload_label(asset).is_some_and(|preload| preload == "eager");
    RouteCost::new(route, cost_units).with_critical(critical)
}

fn package_cost_units(cost: &str) -> u32 {
    match cost {
        "high" => 3,
        "medium" => 2,
        "low" => 1,
        _ => 1,
    }
}

fn infer_package_id(asset_id: &str, package_ids: &[String]) -> String {
    if let Some((package, _)) = asset_id.split_once('.')
        && !package.trim().is_empty()
    {
        return package.to_string();
    }
    if package_ids.len() == 1 {
        return package_ids[0].clone();
    }
    "runtime".to_string()
}

fn query_version(path: &str) -> Option<&str> {
    path.split_once('?').and_then(|(_, query)| {
        query.split('&').find_map(|part| {
            let (key, value) = part.split_once('=')?;
            (key == "v" || key == "version").then_some(value)
        })
    })
}

fn json_string<'a>(value: &'a serde_json::Value, keys: &[&str]) -> Option<&'a str> {
    keys.iter()
        .find_map(|key| value.get(*key).and_then(serde_json::Value::as_str))
}

fn json_u64(value: &serde_json::Value, keys: &[&str]) -> Option<u64> {
    keys.iter()
        .find_map(|key| value.get(*key))
        .and_then(|value| {
            value
                .as_u64()
                .or_else(|| value.as_str().and_then(|value| value.parse().ok()))
        })
}

fn route_package_cost(packages: &[String], render_lanes: &BTreeSet<String>) -> &'static str {
    let package_count = packages.len();
    let has_heavy_package = packages
        .iter()
        .any(|package| matches!(package.as_str(), "shader" | "workertown" | "morph"));
    let has_heavy_lane = render_lanes.iter().any(|lane| {
        lane.contains("shader")
            || lane.contains("worker")
            || lane.contains("render")
            || lane.contains("webgpu")
    });
    if package_count >= 4 || (has_heavy_package && has_heavy_lane) {
        "high"
    } else if package_count >= 2 || has_heavy_package || has_heavy_lane {
        "medium"
    } else {
        "low"
    }
}

fn route_package_cost_cache_key(packages: &[String], render_lanes: &BTreeSet<String>) -> String {
    let mut packages = packages.to_vec();
    packages.sort();
    format!(
        "pkg={};lanes={}",
        packages.join(","),
        render_lanes.iter().cloned().collect::<Vec<_>>().join(",")
    )
}

fn route_compression_candidates<'a>(values: impl Iterator<Item = &'a String>) -> Vec<String> {
    let mut candidates = BTreeSet::new();
    for value in values {
        let path = value.split('?').next().unwrap_or(value.as_str());
        if path.ends_with(".js")
            || path.ends_with(".css")
            || path.ends_with(".json")
            || path.ends_with(".wasm")
        {
            candidates.insert(value.clone());
        }
    }
    candidates.into_iter().collect()
}

fn route_dictionary_candidates(compression_candidates: &[String]) -> Vec<String> {
    compression_candidates
        .iter()
        .filter(|path| path.ends_with(".js") || path.contains(".js?") || path.ends_with(".json"))
        .cloned()
        .collect()
}

fn json_string_array(values: &[serde_json::Value]) -> Vec<String> {
    values
        .iter()
        .filter_map(serde_json::Value::as_str)
        .map(ToString::to_string)
        .collect()
}

fn extract_route_runtime_json(
    text: &str,
    source: &Path,
) -> Result<Option<serde_json::Value>, String> {
    if source.extension().and_then(|value| value.to_str()) == Some("json") {
        return serde_json::from_str(text).map(Some).map_err(|error| {
            format!(
                "Failed to parse route runtime JSON {}: {error}",
                source.display()
            )
        });
    }
    let Some(id_index) = text.find("__DXR_ROUTE_RUNTIME__") else {
        return Ok(None);
    };
    let after_id = &text[id_index..];
    let Some(open_offset) = after_id.find('>') else {
        return Err(format!(
            "Malformed route runtime script in {}",
            source.display()
        ));
    };
    let body_start = id_index + open_offset + 1;
    let Some(close_offset) = text[body_start..].find("</script>") else {
        return Err(format!(
            "Unclosed route runtime script in {}",
            source.display()
        ));
    };
    let body = &text[body_start..body_start + close_offset];
    serde_json::from_str(body.trim())
        .map(Some)
        .map_err(|error| {
            format!(
                "Failed to parse route runtime script {}: {error}",
                source.display()
            )
        })
}

fn collect_route_runtime_sources(input: &Path) -> Result<Vec<RouteRuntimeSource>, String> {
    let source_paths = collect_route_plan_sources(input)?;
    if source_paths.is_empty() {
        return Err(format!(
            "No route runtime payload sources found under {}",
            input.display()
        ));
    }

    let sources = load_route_runtime_sources_bounded(source_paths)?;

    if sources.is_empty() {
        return Err("No __DXR_ROUTE_RUNTIME__ payloads found".to_string());
    }
    Ok(sources)
}

fn load_route_runtime_sources_bounded(
    source_paths: Vec<PathBuf>,
) -> Result<Vec<RouteRuntimeSource>, String> {
    let concurrency = bounded_scan_concurrency(source_paths.len());
    if concurrency <= 1 || source_paths.len() <= 1 {
        let mut sources = Vec::new();
        for source in source_paths {
            if let Some(runtime_source) = load_route_runtime_source(&source)? {
                sources.push(runtime_source);
            }
        }
        return Ok(sources);
    }

    let indexed = source_paths
        .into_iter()
        .enumerate()
        .collect::<Vec<(usize, PathBuf)>>();
    let chunk_size = indexed.len().div_ceil(concurrency);
    let chunks = indexed
        .chunks(chunk_size)
        .map(|chunk| chunk.to_vec())
        .collect::<Vec<_>>();
    let mut ordered: Vec<Option<Result<Option<RouteRuntimeSource>, String>>> =
        (0..chunks.iter().map(Vec::len).sum())
            .map(|_| None)
            .collect();

    thread::scope(|scope| {
        let mut handles = Vec::new();
        for chunk in chunks {
            handles.push(scope.spawn(move || {
                chunk
                    .into_iter()
                    .map(|(index, path)| (index, load_route_runtime_source(&path)))
                    .collect::<Vec<_>>()
            }));
        }
        for handle in handles {
            for (index, result) in handle.join().expect("route runtime worker panicked") {
                ordered[index] = Some(result);
            }
        }
    });

    let mut sources = Vec::new();
    for result in ordered.into_iter().flatten() {
        if let Some(runtime_source) = result? {
            sources.push(runtime_source);
        }
    }
    Ok(sources)
}

fn bounded_scan_concurrency(item_count: usize) -> usize {
    let available = thread::available_parallelism()
        .map(usize::from)
        .unwrap_or(1);
    available.min(4).min(item_count.max(1))
}

fn collect_route_plan_sources(input: &Path) -> Result<Vec<PathBuf>, String> {
    let mut sources = Vec::new();
    if input.is_file() {
        sources.push(input.to_path_buf());
        return Ok(sources);
    }
    if !input.is_dir() {
        return Err(format!("Input path does not exist: {}", input.display()));
    }
    collect_route_plan_sources_inner(input, input, &mut sources)?;
    sources.sort();
    Ok(sources)
}

fn collect_route_plan_sources_inner(
    root: &Path,
    current: &Path,
    sources: &mut Vec<PathBuf>,
) -> Result<(), String> {
    for entry in fs::read_dir(current).map_err(|error| {
        format!(
            "Failed to read route plan directory {} under {}: {error}",
            current.display(),
            root.display()
        )
    })? {
        let entry = entry.map_err(|error| format!("Failed to read route plan entry: {error}"))?;
        let path = entry.path();
        let file_type = entry
            .file_type()
            .map_err(|error| format!("Failed to inspect {}: {error}", path.display()))?;
        if file_type.is_dir() {
            collect_route_plan_sources_inner(root, &path, sources)?;
        } else if matches!(
            path.extension().and_then(|value| value.to_str()),
            Some("html" | "json")
        ) {
            sources.push(path);
        }
    }
    Ok(())
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum GzipMode {
    Zopfli,
    Fast,
}

#[derive(Debug)]
struct PrecompressOptions {
    asset_dir: PathBuf,
    extensions: Vec<String>,
    dry_run: bool,
    brotli: bool,
    gzip: bool,
    zstd: bool,
    shared_dictionary: bool,
    recurse: bool,
    brotli_quality: u32,
    gzip_mode: GzipMode,
    zstd_level: i32,
}

const SHARED_DICTIONARY_FILE: &str = "compression-dictionary.dcd";
const SHARED_DICTIONARY_MANIFEST: &str = "compression-dictionary.dcd-manifest";
const SHARED_DICTIONARY_MAX_BYTES: usize = 32 * 1024;
const SHARED_DICTIONARY_MIN_TOKEN_BYTES: usize = 8;

fn precompress(args: Vec<String>) -> Result<(), String> {
    let mut asset_dir: Option<PathBuf> = None;
    let mut extensions: Vec<String> = default_precompress_extensions();
    let mut dry_run = false;
    let mut brotli = true;
    let mut gzip = true;
    let mut zstd = true;
    let mut shared_dictionary = false;
    let mut recurse = true;
    let mut brotli_quality = 11u32;
    let mut gzip_mode = GzipMode::Zopfli;
    let mut zstd_level = 19i32;

    let mut index = 0usize;
    while index < args.len() {
        match args[index].as_str() {
            "--asset-dir" => {
                index += 1;
                asset_dir = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--asset-dir requires a path".to_string())?,
                ));
            }
            "--extensions" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--extensions requires a comma-separated list".to_string())?;
                extensions = parse_extensions([value.as_str()])?;
            }
            "--dry-run" => dry_run = true,
            "--no-brotli" => brotli = false,
            "--no-gzip" => gzip = false,
            "--no-zstd" => zstd = false,
            "--shared-dictionary" | "--dictionary" | "--compression-dictionary" => {
                shared_dictionary = true;
            }
            "--no-recurse" => recurse = false,
            "--brotli-quality" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--brotli-quality requires a value".to_string())?;
                brotli_quality = value
                    .parse::<u32>()
                    .map_err(|_| "--brotli-quality must be an integer".to_string())?;
                if brotli_quality > 11 {
                    return Err("--brotli-quality must be between 0 and 11".to_string());
                }
            }
            "--gzip-mode" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--gzip-mode requires `zopfli` or `fast`".to_string())?;
                gzip_mode = match value.as_str() {
                    "zopfli" => GzipMode::Zopfli,
                    "fast" => GzipMode::Fast,
                    other => {
                        return Err(format!(
                            "Unknown gzip mode `{other}`. Expected `zopfli` or `fast`."
                        ));
                    }
                };
            }
            "--zstd-level" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--zstd-level requires a value".to_string())?;
                zstd_level = value
                    .parse::<i32>()
                    .map_err(|_| "--zstd-level must be an integer".to_string())?;
                if !(-7..=22).contains(&zstd_level) {
                    return Err("--zstd-level must be between -7 and 22".to_string());
                }
            }
            "--help" | "-h" => return Err(help_precompress()),
            other => {
                return Err(format!(
                    "Unknown precompress flag `{other}`.\n{}",
                    help_precompress()
                ));
            }
        }
        index += 1;
    }

    let options = PrecompressOptions {
        asset_dir: asset_dir.ok_or_else(help_precompress)?,
        extensions,
        dry_run,
        brotli,
        gzip,
        zstd,
        shared_dictionary,
        recurse,
        brotli_quality,
        gzip_mode,
        zstd_level,
    };
    run_precompress(options)
}

fn hash_asset(args: Vec<String>) -> Result<(), String> {
    let mut input: Option<PathBuf> = None;
    let mut length = 16usize;
    let mut algorithm = ContentHashAlgorithm::Blake3;

    let mut index = 0usize;
    while index < args.len() {
        match args[index].as_str() {
            "--input" | "-i" => {
                index += 1;
                input = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--input requires a path".to_string())?,
                ));
            }
            "--length" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--length requires a value".to_string())?;
                length = value
                    .parse::<usize>()
                    .map_err(|_| "--length must be an integer".to_string())?;
                if !(8..=64).contains(&length) {
                    return Err("--length must be between 8 and 64".to_string());
                }
            }
            "--strategy" | "--algorithm" => {
                index += 1;
                algorithm = parse_hash_algorithm(
                    args.get(index)
                        .ok_or_else(|| "--strategy requires blake3 or sha256".to_string())?,
                )?;
            }
            "--help" | "-h" => return Err(help_hash()),
            other => return Err(format!("Unknown hash flag `{other}`.\n{}", help_hash())),
        }
        index += 1;
    }

    let input = input.ok_or_else(help_hash)?;
    let mut cache = FileContentHashCache::default();
    let digest = cache.hash_file(&input, AssetHashStrategy::new(algorithm, length))?;
    println!("{digest}");
    Ok(())
}

fn parse_hash_algorithm(value: &str) -> Result<ContentHashAlgorithm, String> {
    match value {
        "blake3" | "b3" => Ok(ContentHashAlgorithm::Blake3),
        "sha256" | "sha-256" => Ok(ContentHashAlgorithm::Sha256),
        other => Err(format!(
            "Unknown hash strategy `{other}`. Expected blake3 or sha256."
        )),
    }
}

fn css_minify(args: Vec<String>) -> Result<(), String> {
    let mut input: Option<PathBuf> = None;
    let mut output: Option<PathBuf> = None;
    let mut targets: Vec<String> = Vec::new();
    let mut error_recovery = false;
    let mut check = false;

    let mut index = 0usize;
    while index < args.len() {
        match args[index].as_str() {
            "--input" | "-i" => {
                index += 1;
                input = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--input requires a path".to_string())?,
                ));
            }
            "--output" | "-o" => {
                index += 1;
                output = Some(PathBuf::from(
                    args.get(index)
                        .ok_or_else(|| "--output requires a path".to_string())?,
                ));
            }
            "--targets" => {
                index += 1;
                let value = args
                    .get(index)
                    .ok_or_else(|| "--targets requires a browserslist query".to_string())?;
                targets.extend(
                    value
                        .split(',')
                        .map(str::trim)
                        .filter(|target| !target.is_empty())
                        .map(str::to_string),
                );
            }
            "--error-recovery" => error_recovery = true,
            "--check" => check = true,
            "--help" | "-h" => return Err(help_css_minify()),
            other => {
                return Err(format!(
                    "Unknown css-minify flag `{other}`.\n{}",
                    help_css_minify()
                ));
            }
        }
        index += 1;
    }

    let input = input.ok_or_else(help_css_minify)?;
    let source = fs::read_to_string(&input)
        .map_err(|error| format!("Failed to read {}: {error}", input.display()))?;
    let optimized = minify_css(
        &source,
        CssMinifyOptions::default()
            .with_filename(input.display().to_string())
            .with_targets(targets)
            .with_error_recovery(error_recovery),
    )
    .map_err(|error| error.to_string())?;

    let Some(output) = output else {
        print!("{optimized}");
        return Ok(());
    };

    if check {
        let current = fs::read_to_string(&output)
            .map_err(|error| format!("Failed to read {}: {error}", output.display()))?;
        if current != optimized {
            return Err(format!(
                "{} is stale. Re-run `dxr-assets css-minify --input {} --output {}`.",
                output.display(),
                input.display(),
                output.display()
            ));
        }
        return Ok(());
    }

    write_output(&output, optimized.as_bytes())
        .map_err(|error| format!("Failed to write {}: {error}", output.display()))
}

fn write_output(path: &Path, bytes: &[u8]) -> std::io::Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let stamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_nanos();
    let temp = path.with_extension(format!("tmp-{stamp}"));
    fs::write(&temp, bytes)?;
    if path.exists() {
        fs::remove_file(path)?;
    }
    fs::rename(temp, path)
}

fn run_precompress(options: PrecompressOptions) -> Result<(), String> {
    if !options.asset_dir.is_dir() {
        return Err(format!(
            "Asset directory not found: {}",
            options.asset_dir.display()
        ));
    }

    let sources =
        collect_precompress_sources(&options.asset_dir, &options.extensions, options.recurse)?;

    let mut generated = 0usize;
    let mut would_generate = 0usize;
    let mut skipped_current = 0usize;
    let mut dictionary_outcome = None;
    let mut failed: Vec<String> = Vec::new();
    let mut compression_cache = CompressionOutputCache::default();
    let mut dictionary_cache = DictionaryOutputCache::default();

    for source in &sources {
        let bytes = fs::read(source)
            .map_err(|error| format!("Failed to read {}: {error}", source.display()))?;
        let content_hash = content_hash_version(&bytes);

        if options.brotli {
            let output = source.with_extension(format!(
                "{}.br",
                source
                    .extension()
                    .and_then(|extension| extension.to_str())
                    .unwrap_or_default()
            ));
            match maybe_write_compressed_output(source, &output, options.dry_run, || {
                compression_cache.compress(
                    &content_hash,
                    CompressionAlgorithm::Brotli(options.brotli_quality),
                    || brotli_compress(&bytes, options.brotli_quality),
                )
            }) {
                Ok(WriteOutcome::Generated) => generated += 1,
                Ok(WriteOutcome::WouldGenerate) => would_generate += 1,
                Ok(WriteOutcome::SkippedCurrent) => skipped_current += 1,
                Err(error) => failed.push(format!("br {}: {error}", source.display())),
            }
        }

        if options.gzip {
            let output = source.with_extension(format!(
                "{}.gz",
                source
                    .extension()
                    .and_then(|extension| extension.to_str())
                    .unwrap_or_default()
            ));
            match maybe_write_compressed_output(source, &output, options.dry_run, || {
                compression_cache.compress(
                    &content_hash,
                    CompressionAlgorithm::Gzip(options.gzip_mode),
                    || gzip_compress(&bytes, options.gzip_mode),
                )
            }) {
                Ok(WriteOutcome::Generated) => generated += 1,
                Ok(WriteOutcome::WouldGenerate) => would_generate += 1,
                Ok(WriteOutcome::SkippedCurrent) => skipped_current += 1,
                Err(error) => failed.push(format!("gz {}: {error}", source.display())),
            }
        }

        if options.zstd {
            let output = source.with_extension(format!(
                "{}.zst",
                source
                    .extension()
                    .and_then(|extension| extension.to_str())
                    .unwrap_or_default()
            ));
            match maybe_write_compressed_output(source, &output, options.dry_run, || {
                compression_cache.compress(
                    &content_hash,
                    CompressionAlgorithm::Zstd(options.zstd_level),
                    || zstd_compress(&bytes, options.zstd_level),
                )
            }) {
                Ok(WriteOutcome::Generated) => generated += 1,
                Ok(WriteOutcome::WouldGenerate) => would_generate += 1,
                Ok(WriteOutcome::SkippedCurrent) => skipped_current += 1,
                Err(error) => failed.push(format!("zst {}: {error}", source.display())),
            }
        }
    }

    if options.shared_dictionary {
        match maybe_write_shared_dictionary(
            &options.asset_dir,
            &sources,
            options.dry_run,
            &mut dictionary_cache,
        ) {
            Ok(outcome) => dictionary_outcome = Some(outcome),
            Err(error) => failed.push(format!("dictionary: {error}")),
        }
    }

    println!("Precompression summary");
    println!("  Asset directory: {}", options.asset_dir.display());
    println!("  Extensions: {}", options.extensions.join(", "));
    println!(
        "  Brotli: {}",
        if options.brotli {
            format!("Rust brotli (quality {})", options.brotli_quality)
        } else {
            "disabled".to_string()
        }
    );
    println!(
        "  Gzip: {}",
        if options.gzip {
            match options.gzip_mode {
                GzipMode::Zopfli => "Rust zopfli".to_string(),
                GzipMode::Fast => "Rust flate2".to_string(),
            }
        } else {
            "disabled".to_string()
        }
    );
    println!(
        "  Zstd: {}",
        if options.zstd {
            format!("Rust zstd (level {})", options.zstd_level)
        } else {
            "disabled".to_string()
        }
    );
    println!("  Sources scanned: {}", sources.len());
    if let Some(outcome) = dictionary_outcome {
        println!(
            "  Shared dictionary: {} ({} assets, {} bytes)",
            outcome.status_label(),
            outcome.asset_count,
            outcome.dictionary_bytes
        );
        println!("  Dictionary manifest: {}", outcome.manifest.display());
    }
    println!("  Generated: {generated}");
    println!("  Would generate: {would_generate}");
    println!("  Skipped current: {skipped_current}");
    println!("  Failed: {}", failed.len());

    if !failed.is_empty() {
        for error in failed.iter().take(12) {
            eprintln!("  {error}");
        }
        return Err("precompression failed".to_string());
    }

    Ok(())
}

#[derive(Debug, Clone)]
struct SharedDictionaryOutcome {
    status: SharedDictionaryStatus,
    asset_count: usize,
    dictionary_bytes: usize,
    manifest: PathBuf,
}

impl SharedDictionaryOutcome {
    fn status_label(&self) -> &'static str {
        match self.status {
            SharedDictionaryStatus::Generated => "generated",
            SharedDictionaryStatus::WouldGenerate => "would generate",
            SharedDictionaryStatus::SkippedNoEligibleAssets => "skipped; no eligible assets",
            SharedDictionaryStatus::SkippedNoRepeatedTokens => "skipped; no repeated tokens",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SharedDictionaryStatus {
    Generated,
    WouldGenerate,
    SkippedNoEligibleAssets,
    SkippedNoRepeatedTokens,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WriteOutcome {
    Generated,
    WouldGenerate,
    SkippedCurrent,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
enum CompressionAlgorithm {
    Brotli(u32),
    Gzip(GzipMode),
    Zstd(i32),
}

#[derive(Debug, Default)]
struct CompressionOutputCache {
    outputs: BTreeMap<(String, CompressionAlgorithm), Vec<u8>>,
}

impl CompressionOutputCache {
    fn compress<F>(
        &mut self,
        content_hash: &str,
        algorithm: CompressionAlgorithm,
        compress: F,
    ) -> Result<Vec<u8>, String>
    where
        F: FnOnce() -> Result<Vec<u8>, String>,
    {
        let key = (content_hash.to_string(), algorithm);
        if let Some(output) = self.outputs.get(&key) {
            return Ok(output.clone());
        }
        let output = compress()?;
        self.outputs.insert(key, output.clone());
        Ok(output)
    }
}

#[derive(Debug, Default)]
struct DictionaryOutputCache {
    outputs: BTreeMap<String, Vec<u8>>,
}

fn maybe_write_compressed_output<F>(
    source: &Path,
    output: &Path,
    dry_run: bool,
    compress: F,
) -> Result<WriteOutcome, String>
where
    F: FnOnce() -> Result<Vec<u8>, String>,
{
    if is_fresh_output(source, output).map_err(|error| {
        format!(
            "Failed to check freshness for {}: {error}",
            output.display()
        )
    })? {
        return Ok(WriteOutcome::SkippedCurrent);
    }

    if dry_run {
        return Ok(WriteOutcome::WouldGenerate);
    }

    let compressed = compress()?;
    write_output(output, &compressed)
        .map_err(|error| format!("Failed to write {}: {error}", output.display()))?;
    Ok(WriteOutcome::Generated)
}

fn is_fresh_output(source: &Path, output: &Path) -> std::io::Result<bool> {
    if !output.is_file() {
        return Ok(false);
    }
    let source_modified = fs::metadata(source)?.modified()?;
    let output_modified = fs::metadata(output)?.modified()?;
    Ok(output_modified >= source_modified)
}

fn brotli_compress(bytes: &[u8], quality: u32) -> Result<Vec<u8>, String> {
    let mut output = Vec::new();
    {
        let mut writer = brotli::CompressorWriter::new(&mut output, 4096, quality, 22);
        writer
            .write_all(bytes)
            .map_err(|error| format!("Brotli compression failed: {error}"))?;
    }
    Ok(output)
}

fn gzip_compress(bytes: &[u8], mode: GzipMode) -> Result<Vec<u8>, String> {
    match mode {
        GzipMode::Zopfli => {
            let mut input = Cursor::new(bytes);
            let mut output = Vec::new();
            zopfli::compress(
                zopfli::Options::default(),
                zopfli::Format::Gzip,
                &mut input,
                &mut output,
            )
            .map_err(|error| format!("Zopfli gzip compression failed: {error}"))?;
            Ok(output)
        }
        GzipMode::Fast => {
            let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
            encoder
                .write_all(bytes)
                .map_err(|error| format!("Gzip compression failed: {error}"))?;
            encoder
                .finish()
                .map_err(|error| format!("Gzip finish failed: {error}"))
        }
    }
}

fn zstd_compress(bytes: &[u8], level: i32) -> Result<Vec<u8>, String> {
    zstd::stream::encode_all(Cursor::new(bytes), level)
        .map_err(|error| format!("Zstd compression failed: {error}"))
}

fn maybe_write_shared_dictionary(
    asset_dir: &Path,
    sources: &[PathBuf],
    dry_run: bool,
    cache: &mut DictionaryOutputCache,
) -> Result<SharedDictionaryOutcome, String> {
    let candidates = collect_dictionary_candidates(asset_dir, sources)?;
    let dictionary_path = asset_dir.join(SHARED_DICTIONARY_FILE);
    let manifest_path = asset_dir.join(SHARED_DICTIONARY_MANIFEST);

    if candidates.is_empty() {
        return Ok(SharedDictionaryOutcome {
            status: SharedDictionaryStatus::SkippedNoEligibleAssets,
            asset_count: 0,
            dictionary_bytes: 0,
            manifest: manifest_path,
        });
    }

    let dictionary = build_shared_dictionary_cached(&candidates, cache);
    if dictionary.is_empty() {
        return Ok(SharedDictionaryOutcome {
            status: SharedDictionaryStatus::SkippedNoRepeatedTokens,
            asset_count: candidates.len(),
            dictionary_bytes: 0,
            manifest: manifest_path,
        });
    }

    let manifest =
        shared_dictionary_manifest(asset_dir, &dictionary_path, &dictionary, &candidates)?;
    if dry_run {
        return Ok(SharedDictionaryOutcome {
            status: SharedDictionaryStatus::WouldGenerate,
            asset_count: candidates.len(),
            dictionary_bytes: dictionary.len(),
            manifest: manifest_path,
        });
    }

    write_output(&dictionary_path, &dictionary).map_err(|error| {
        format!(
            "Failed to write dictionary {}: {error}",
            dictionary_path.display()
        )
    })?;
    write_output(&manifest_path, manifest.as_bytes()).map_err(|error| {
        format!(
            "Failed to write dictionary manifest {}: {error}",
            manifest_path.display()
        )
    })?;

    Ok(SharedDictionaryOutcome {
        status: SharedDictionaryStatus::Generated,
        asset_count: candidates.len(),
        dictionary_bytes: dictionary.len(),
        manifest: manifest_path,
    })
}

#[derive(Debug, Clone)]
struct DictionaryCandidate {
    relative_path: String,
    extension: String,
    bytes: Vec<u8>,
}

fn collect_dictionary_candidates(
    asset_dir: &Path,
    sources: &[PathBuf],
) -> Result<Vec<DictionaryCandidate>, String> {
    let mut candidates = Vec::new();
    for source in sources {
        let extension = source
            .extension()
            .and_then(|extension| extension.to_str())
            .map(|extension| format!(".{}", extension.to_ascii_lowercase()))
            .unwrap_or_default();
        if !is_dictionary_extension(&extension) {
            continue;
        }

        let bytes = fs::read(source)
            .map_err(|error| format!("Failed to read {}: {error}", source.display()))?;
        if bytes.is_empty() {
            continue;
        }

        candidates.push(DictionaryCandidate {
            relative_path: relative_asset_path(asset_dir, source)?,
            extension,
            bytes,
        });
    }

    candidates.sort_by(|left, right| left.relative_path.cmp(&right.relative_path));
    Ok(candidates)
}

fn is_dictionary_extension(extension: &str) -> bool {
    matches!(
        extension,
        ".css" | ".html" | ".js" | ".json" | ".map" | ".mjs" | ".svg" | ".txt" | ".xml"
    )
}

fn build_shared_dictionary(candidates: &[DictionaryCandidate]) -> Vec<u8> {
    let mut tokens: Vec<(Vec<u8>, usize)> = Vec::new();
    for (asset_index, candidate) in candidates.iter().enumerate() {
        tokens.extend(
            dictionary_tokens(&candidate.bytes)
                .into_iter()
                .map(|token| (token, asset_index)),
        );
    }
    tokens.sort();

    let mut selected = Vec::new();
    let mut index = 0usize;
    while index < tokens.len() {
        let token = &tokens[index].0;
        let mut asset_count = 1usize;
        let mut previous_asset_index = tokens[index].1;
        let mut count = 1usize;
        while index + count < tokens.len() && tokens[index + count].0 == *token {
            let asset_index = tokens[index + count].1;
            if asset_index != previous_asset_index {
                asset_count += 1;
                previous_asset_index = asset_index;
            }
            count += 1;
        }

        if asset_count > 1 {
            if !selected.is_empty() {
                selected.push(b'\n');
            }
            let remaining = SHARED_DICTIONARY_MAX_BYTES.saturating_sub(selected.len());
            if remaining == 0 {
                break;
            }
            selected.extend(token.iter().copied().take(remaining));
        }

        if selected.len() >= SHARED_DICTIONARY_MAX_BYTES {
            break;
        }
        index += count;
    }

    selected
}

fn build_shared_dictionary_cached(
    candidates: &[DictionaryCandidate],
    cache: &mut DictionaryOutputCache,
) -> Vec<u8> {
    let key = dictionary_group_cache_key(candidates);
    if let Some(dictionary) = cache.outputs.get(&key) {
        return dictionary.clone();
    }
    let dictionary = build_shared_dictionary(candidates);
    cache.outputs.insert(key, dictionary.clone());
    dictionary
}

fn dictionary_group_cache_key(candidates: &[DictionaryCandidate]) -> String {
    let mut descriptor = String::from("dictionary:v1\n");
    for candidate in candidates {
        descriptor.push_str(&candidate.relative_path);
        descriptor.push('\n');
        descriptor.push_str(&content_hash_version(&candidate.bytes));
        descriptor.push('\n');
    }
    content_hash_version(descriptor.as_bytes())
}

fn dictionary_tokens(bytes: &[u8]) -> Vec<Vec<u8>> {
    bytes
        .split(|byte| !is_dictionary_token_byte(*byte))
        .filter(|token| token.len() >= SHARED_DICTIONARY_MIN_TOKEN_BYTES)
        .map(|token| token.to_vec())
        .collect()
}

fn is_dictionary_token_byte(byte: u8) -> bool {
    byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b':' | b'/')
}

fn shared_dictionary_manifest(
    asset_dir: &Path,
    dictionary_path: &Path,
    dictionary: &[u8],
    candidates: &[DictionaryCandidate],
) -> Result<String, String> {
    let mut manifest = String::new();
    let dictionary_relative = relative_asset_path(asset_dir, dictionary_path)?;
    let dictionary_hash = blake3::hash(dictionary).to_hex().to_string();

    manifest.push_str("{\n");
    manifest.push_str("  \"version\": 1,\n");
    manifest.push_str("  \"kind\": \"compression-dictionary-transport-prototype\",\n");
    manifest.push_str("  \"dictionary\": {\n");
    manifest.push_str(&format!(
        "    \"path\": \"{}\",\n",
        json_escape(&dictionary_relative)
    ));
    manifest.push_str(&format!("    \"bytes\": {},\n", dictionary.len()));
    manifest.push_str(&format!("    \"blake3\": \"{}\"\n", dictionary_hash));
    manifest.push_str("  },\n");
    manifest.push_str("  \"assets\": [\n");
    for (index, candidate) in candidates.iter().enumerate() {
        manifest.push_str("    {\n");
        manifest.push_str(&format!(
            "      \"path\": \"{}\",\n",
            json_escape(&candidate.relative_path)
        ));
        manifest.push_str(&format!(
            "      \"extension\": \"{}\",\n",
            json_escape(&candidate.extension)
        ));
        manifest.push_str(&format!("      \"bytes\": {}\n", candidate.bytes.len()));
        manifest.push_str("    }");
        if index + 1 < candidates.len() {
            manifest.push(',');
        }
        manifest.push('\n');
    }
    manifest.push_str("  ]\n");
    manifest.push_str("}\n");

    Ok(manifest)
}

fn relative_asset_path(root: &Path, path: &Path) -> Result<String, String> {
    let relative = path.strip_prefix(root).map_err(|error| {
        format!(
            "Failed to make {} relative to {}: {error}",
            path.display(),
            root.display()
        )
    })?;
    Ok(relative
        .components()
        .map(|component| component.as_os_str().to_string_lossy())
        .collect::<Vec<_>>()
        .join("/"))
}

fn json_escape(value: &str) -> String {
    let mut escaped = String::new();
    for character in value.chars() {
        match character {
            '"' => escaped.push_str("\\\""),
            '\\' => escaped.push_str("\\\\"),
            '\n' => escaped.push_str("\\n"),
            '\r' => escaped.push_str("\\r"),
            '\t' => escaped.push_str("\\t"),
            character if character.is_control() => {
                escaped.push_str(&format!("\\u{:04x}", character as u32));
            }
            character => escaped.push(character),
        }
    }
    escaped
}

fn collect_precompress_sources(
    root: &Path,
    extensions: &[String],
    recurse: bool,
) -> Result<Vec<PathBuf>, String> {
    let mut sources = Vec::new();
    collect_precompress_sources_inner(root, root, extensions, recurse, &mut sources)?;
    sources.sort();
    Ok(sources)
}

fn collect_precompress_sources_inner(
    root: &Path,
    current: &Path,
    extensions: &[String],
    recurse: bool,
    sources: &mut Vec<PathBuf>,
) -> Result<(), String> {
    for entry in fs::read_dir(current)
        .map_err(|error| format!("Failed to read {}: {error}", current.display()))?
    {
        let entry = entry
            .map_err(|error| format!("Failed to read {} entry: {error}", current.display()))?;
        let path = entry.path();
        let metadata = entry
            .metadata()
            .map_err(|error| format!("Failed to stat {}: {error}", path.display()))?;
        if metadata.is_dir() {
            if recurse {
                collect_precompress_sources_inner(root, &path, extensions, recurse, sources)?;
            }
            continue;
        }
        if !metadata.is_file() {
            continue;
        }
        let extension = path
            .extension()
            .and_then(|extension| extension.to_str())
            .map(|extension| format!(".{}", extension.to_ascii_lowercase()));
        if extension
            .as_ref()
            .is_some_and(|extension| extensions.contains(extension))
        {
            sources.push(path);
        }
    }
    if current == root && sources.is_empty() {
        return Ok(());
    }
    Ok(())
}

fn default_precompress_extensions() -> Vec<String> {
    parse_extensions([
        ".css", ".html", ".js", ".json", ".map", ".mjs", ".svg", ".txt", ".wasm", ".xml",
    ])
    .expect("default extensions")
}

fn parse_extensions<'a>(values: impl IntoIterator<Item = &'a str>) -> Result<Vec<String>, String> {
    let mut extensions: Vec<String> = Vec::new();
    for value in values {
        for part in value.split(',') {
            let mut extension = part.trim().to_ascii_lowercase();
            if extension.is_empty() {
                continue;
            }
            if !extension.starts_with('.') {
                extension.insert(0, '.');
            }
            if extension == ".br" || extension == ".gz" || extension == ".zst" {
                continue;
            }
            if !extensions.contains(&extension) {
                extensions.push(extension);
            }
        }
    }
    if extensions.is_empty() {
        return Err("At least one non-compressed extension must be provided.".to_string());
    }
    Ok(extensions)
}

fn help() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets css-minify|css --input <path> [--output <path>] [--targets <query>] [--check]\n",
        "  dxr-assets precompress|zip --asset-dir <path> [--extensions .js,.css] [--dry-run] [--shared-dictionary]\n",
        "  dxr-assets budget-audit|budget --input <file-or-dir> [--budget-bytes <n>] [--route-budget-bytes <n>]\n",
        "  dxr-assets route-plan|routes --input <file-or-dir> --output <path> [--budget-bytes <n>]\n",
        "  dxr-assets hash --input <path> [--length 16]\n"
    )
    .to_string()
}

fn help_css_minify() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets css-minify|css --input <path> [--output <path>]\n\n",
        "Flags:\n",
        "  --input, -i <path>       Source CSS file.\n",
        "  --output, -o <path>     Destination CSS file. Omit to print to stdout.\n",
        "  --targets <query>       Optional browserslist target query. Repeat or comma-separate.\n",
        "  --error-recovery        Let Lightning CSS recover from parser errors.\n",
        "  --check                 Compare optimized output with --output and fail if stale.\n"
    )
    .to_string()
}

fn help_precompress() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets precompress|zip --asset-dir <path> [--extensions .js,.css] [--dry-run] [--shared-dictionary]\n\n",
        "Flags:\n",
        "  --asset-dir <path>         Directory containing assets.\n",
        "  --extensions <list>        Comma-separated file extensions.\n",
        "  --dry-run                  Print summary without writing files.\n",
        "  --no-brotli                Skip .br output.\n",
        "  --no-gzip                  Skip .gz output.\n",
        "  --no-zstd                  Skip .zst output.\n",
        "  --no-recurse               Only scan direct children.\n",
        "  --brotli-quality <0-11>    Brotli quality. Defaults to 11.\n",
        "  --gzip-mode <mode>         zopfli or fast. Defaults to zopfli.\n",
        "  --zstd-level <-7-22>       Zstd level. Defaults to 19.\n",
        "  --shared-dictionary        Emit a deterministic shared dictionary and manifest.\n",
        "                             Aliases: --dictionary, --compression-dictionary.\n"
    )
    .to_string()
}

fn help_route_plan() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets route-plan|routes --input <file-or-dir> --output <path> [--budget-bytes <n>]\n\n",
        "Flags:\n",
        "  --input <path>         HTML/JSON file or directory containing route runtime payloads.\n",
        "  --output <path>        Destination JSON route asset plan.\n",
        "  --budget-bytes <n>     Optional per-route runtime payload budget.\n",
        "  --duplicate-detection <mode>  off, path, or content-hash. Defaults to path.\n"
    )
    .to_string()
}

fn help_budget_audit() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets budget-audit|budget --input <file-or-dir> [--budget-bytes <n>] [--route-budget-bytes <n>]\n\n",
        "Flags:\n",
        "  --input <path>              HTML/JSON file or directory containing route runtime payloads.\n",
        "  --output <path>             Optional report destination. Omit to print to stdout.\n",
        "  --format <text|json>        CI text output or JSON. Defaults to text.\n",
        "  --budget-bytes <n>          Default per-asset error budget when metadata has no threshold.\n",
        "  --route-budget-bytes <n>    Optional route-level total asset byte budget.\n",
        "  --baseline <path>           JSON route byte baseline for regression warnings.\n",
        "  --include-source-maps       Include .map assets in accounting.\n",
        "  --compact-json              Emit compact JSON when --format json is selected.\n",
        "  --fail-on <level>           warning, error, or never. Defaults to error.\n",
        "  --allow-violations          Alias for --fail-on never.\n"
    )
    .to_string()
}

fn help_hash() -> String {
    concat!(
        "Usage:\n",
        "  dxr-assets hash --input <path> [--length 16]\n\n",
        "Flags:\n",
        "  --input, -i <path>   Source file.\n",
        "  --length <n>         Hex chars to print, 8-64. Defaults to 16.\n",
        "  --strategy <name>    blake3 or sha256. Defaults to blake3.\n"
    )
    .to_string()
}

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

    #[test]
    fn assets_cli_naming_wrappers_match_existing_apis() {
        let adapter = assets_cli_command("budget", ["--input", "public"]);
        assert_eq!(
            adapter,
            AssetsCliCommandAdapter::for_command("budget", ["--input", "public"])
        );
        assert_eq!(
            adapter,
            AssetsCliCommandAdapter::new("budget", ["--input", "public"])
        );
        assert!(run_assets_from(["help"]).is_ok());
    }

    #[test]
    fn css_minify_writes_output() {
        let dir = unique_temp_dir("write");
        fs::create_dir_all(&dir).expect("temp dir");
        let input = dir.join("input.css");
        let output = dir.join("output.css");
        fs::write(&input, ".card { color: rgb(255, 255, 255); }").expect("input");

        run([
            "css-minify",
            "--input",
            input.to_str().unwrap(),
            "--output",
            output.to_str().unwrap(),
        ])
        .expect("minifies");

        let optimized = fs::read_to_string(output).expect("output");
        assert!(optimized.contains(".card"));
        assert!(optimized.len() < 40);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn css_minify_check_detects_stale_output() {
        let dir = unique_temp_dir("check");
        fs::create_dir_all(&dir).expect("temp dir");
        let input = dir.join("input.css");
        let output = dir.join("output.css");
        fs::write(&input, ".card { color: rgb(255, 255, 255); }").expect("input");
        fs::write(&output, ".stale{}").expect("output");

        let result = run([
            "css-minify",
            "--input",
            input.to_str().unwrap(),
            "--output",
            output.to_str().unwrap(),
            "--check",
        ]);

        assert!(result.expect_err("stale output").contains("is stale"));
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn hash_uses_content_not_path() {
        let dir = unique_temp_dir("hash");
        fs::create_dir_all(&dir).expect("temp dir");
        let input = dir.join("runtime.js");
        fs::write(&input, "globalThis.x = 1;").expect("input");

        run(["hash", "--input", input.to_str().unwrap(), "--length", "12"]).expect("hash");

        let first = blake3::hash(b"globalThis.x = 1;").to_hex().to_string();
        let second = blake3::hash(b"globalThis.x = 2;").to_hex().to_string();
        assert_ne!(&first[..12], &second[..12]);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn precompress_generates_brotli_gzip_and_zstd() {
        let dir = unique_temp_dir("precompress");
        fs::create_dir_all(&dir).expect("temp dir");
        let input = dir.join("runtime.js");
        fs::write(
            &input,
            "globalThis.demo='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';".repeat(128),
        )
        .expect("input");

        run([
            "precompress",
            "--asset-dir",
            dir.to_str().unwrap(),
            "--extensions",
            ".js",
            "--gzip-mode",
            "fast",
        ])
        .expect("precompress");

        let source_len = fs::metadata(&input).expect("source metadata").len();
        let br_len = fs::metadata(dir.join("runtime.js.br"))
            .expect("br metadata")
            .len();
        let gz_len = fs::metadata(dir.join("runtime.js.gz"))
            .expect("gz metadata")
            .len();
        let zst_len = fs::metadata(dir.join("runtime.js.zst"))
            .expect("zst metadata")
            .len();
        assert!(br_len < source_len);
        assert!(gz_len < source_len);
        assert!(zst_len < source_len);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn precompress_shared_dictionary_writes_deterministic_manifest() {
        let dir = unique_temp_dir("dictionary");
        fs::create_dir_all(&dir).expect("temp dir");
        fs::write(
            dir.join("app.js"),
            "const shared_cache_key = 'shared_route_token';\n\
             export function render() { return shared_cache_key + 'shared_route_token'; }\n",
        )
        .expect("app js");
        fs::write(
            dir.join("style.css"),
            ":root { --route-name: shared_route_token; }\n\
             .card::before { content: 'shared_cache_key'; }\n",
        )
        .expect("style css");
        fs::write(dir.join("image.wasm"), b"shared_cache_key").expect("wasm");

        let args = [
            "precompress",
            "--asset-dir",
            dir.to_str().unwrap(),
            "--extensions",
            ".js,.css,.wasm",
            "--no-brotli",
            "--no-gzip",
            "--no-zstd",
            "--shared-dictionary",
        ];
        run(args).expect("dictionary precompress");

        let dictionary_path = dir.join(SHARED_DICTIONARY_FILE);
        let manifest_path = dir.join(SHARED_DICTIONARY_MANIFEST);
        let first_dictionary = fs::read_to_string(&dictionary_path).expect("dictionary");
        let first_manifest = fs::read_to_string(&manifest_path).expect("manifest");

        assert_eq!(first_dictionary, "shared_cache_key\nshared_route_token");
        assert!(
            first_manifest.contains("\"kind\": \"compression-dictionary-transport-prototype\"")
        );
        assert!(first_manifest.contains("\"path\": \"compression-dictionary.dcd\""));
        assert!(first_manifest.contains("\"path\": \"app.js\""));
        assert!(first_manifest.contains("\"path\": \"style.css\""));
        assert!(!first_manifest.contains("image.wasm"));
        assert!(first_manifest.contains(&format!("\"bytes\": {}", first_dictionary.len())));

        run(args).expect("dictionary precompress again");

        let second_dictionary = fs::read_to_string(dictionary_path).expect("dictionary again");
        let second_manifest = fs::read_to_string(manifest_path).expect("manifest again");
        assert_eq!(first_dictionary, second_dictionary);
        assert_eq!(first_manifest, second_manifest);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn route_plan_summarizes_route_runtime_payloads_and_duplicates() {
        let dir = unique_temp_dir("route-plan");
        fs::create_dir_all(&dir).expect("temp dir");
        let html = dir.join("route.html");
        let output = dir.join("route-plan.json");
        fs::write(
            &html,
            r#"<script type="application/json" id="__DXR_ROUTE_RUNTIME__">{"v":1,"r":"/shader","a":{"shader.hot":{"path":"/assets/dioprism-shader-hot.js?v=1"},"shader.gallery":{"path":"/assets/dioprism-shader-hot.js?v=1"}},"pkg":{"shader":{"renderLanes":["shader-background"]},"workertown":{"renderLanes":["render"]}},"p":{"route":{"criticalCss":["tailwind","viewtx"],"criticalRuntime":["/dxr-loader.js"],"idleRuntime":["/assets/dioprism-shader-hot.js?v=1"],"visualProfile":"interactive-shader","lcpProtection":"shader-background","speculation":{"prefetch":"viewport","prerender":"none"}}}}</script>"#,
        )
        .expect("route html");

        run([
            "route-plan",
            "--input",
            dir.to_str().unwrap(),
            "--output",
            output.to_str().unwrap(),
            "--budget-bytes",
            "120",
        ])
        .expect("route plan");

        let report = fs::read_to_string(&output).expect("route plan report");
        assert!(report.contains("\"kind\": \"dioxus-route-asset-plan\""));
        assert!(report.contains("\"route\": \"/shader\""));
        assert!(report.contains("\"runtimeContentHash\""));
        assert!(report.contains("\"packages\""));
        assert!(report.contains("\"shader\""));
        assert!(report.contains("\"renderLanes\""));
        assert!(report.contains("\"shader-background\""));
        assert!(report.contains("\"duplicateAssets\""));
        assert!(report.contains("\"count\": 2"));
        assert!(report.contains("\"overBudget\": true"));
        assert!(report.contains("\"criticalCss\""));
        assert!(report.contains("\"tailwind\""));
        assert!(report.contains("\"visualProfile\": \"interactive-shader\""));
        assert!(report.contains("\"lcpProtection\": \"shader-background\""));
        assert!(report.contains("\"packageCost\": \"high\""));
        assert!(report.contains("\"preloadManifest\""));
        assert!(report.contains("\"compressionCandidates\""));
        assert!(report.contains("\"dictionaryCandidates\""));
        assert!(report.contains("\"assetBudgets\""));
        assert!(report.contains("\"routeAssetCosts\""));
        assert!(report.contains("\"prerender\": \"none\""));
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn route_runtime_source_cache_reuses_parsed_manifests() {
        let dir = unique_temp_dir("runtime-cache");
        fs::create_dir_all(&dir).expect("temp dir");
        let json = dir.join("runtime.json");
        let first = r#"{"route":"/first","assetBudgets":[]}"#;
        fs::write(&json, first).expect("first runtime json");

        let mut cache = RouteRuntimeSourceCache::default();
        let first_source = cache
            .load(&json)
            .expect("first load")
            .expect("first runtime");
        fs::write(&json, r#"{"route":"/second","assetBudgets":[]}"#).expect("second runtime json");
        let cached_source = cache
            .load(&json)
            .expect("cached load")
            .expect("cached runtime");

        assert_eq!(cache.len(), 1);
        assert_eq!(
            cached_source
                .runtime
                .get("route")
                .and_then(serde_json::Value::as_str),
            Some("/first")
        );
        assert_eq!(
            first_source.content_hash,
            content_hash_version(first.as_bytes())
        );
        assert_eq!(cached_source.content_hash, first_source.content_hash);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn assets_cli_adapter_config_and_batch_payload_cover_integration_surface() {
        let adapter =
            AssetsCliCommandAdapter::new("budget-audit", ["--input", "public", "--format", "json"])
                .label("owner", "platform");
        let config = assets_cli_config().route_override(
            "/assets",
            dioprism_assets::budget::AssetBudgetRouteOverride::new().enabled(true),
        );
        let report = AssetBudgetAuditReport::from_assets(vec![
            AssetBudget::new("app.css", "assets-cli", AssetKind::Css)
                .with_measured_bytes(128)
                .with_route_cost(RouteCost::new("/assets", 1)),
        ]);
        let payload = assets_cli_batch_payload(&report);

        assert!(config.is_enabled_for_route("/assets"));
        assert!(adapter.explain().contains("budget-audit"));
        assert!(!adapter.cache_key.is_empty());
        assert_eq!(payload.assets.len(), 1);
        assert_eq!(ASSETS_CLI_INTEGRATION_RANK_COVERAGE.len(), 50);
    }

    #[test]
    fn bounded_manifest_loading_preserves_source_order() {
        let dir = unique_temp_dir("bounded-runtime-load");
        fs::create_dir_all(&dir).expect("temp dir");
        let first = dir.join("a.json");
        let second = dir.join("b.json");
        fs::write(&first, r#"{"route":"/a","assetBudgets":[]}"#).expect("first");
        fs::write(&second, r#"{"route":"/b","assetBudgets":[]}"#).expect("second");

        let sources = load_route_runtime_sources_bounded(vec![first.clone(), second.clone()])
            .expect("bounded load");

        assert_eq!(sources.len(), 2);
        assert_eq!(sources[0].source, first);
        assert_eq!(sources[1].source, second);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn content_hash_duplicate_detection_groups_matching_versions() {
        let budgets = vec![
            AssetBudget::new("a.js", "runtime", AssetKind::JavaScript)
                .with_cache_version("same-content"),
            AssetBudget::new("b.js", "runtime", AssetKind::JavaScript)
                .with_cache_version("same-content"),
        ];

        let duplicates =
            duplicate_asset_report(&[], &budgets, DuplicateAssetDetection::ContentHash);

        assert_eq!(duplicates.len(), 1);
        assert_eq!(duplicates[0]["contentHash"], "same-content");
        assert_eq!(duplicates[0]["count"], 2);
    }

    #[test]
    fn streaming_hash_cache_reuses_file_digest() {
        let dir = unique_temp_dir("stream-hash-cache");
        fs::create_dir_all(&dir).expect("temp dir");
        let input = dir.join("runtime.js");
        fs::write(&input, "globalThis.x = 1;").expect("input");
        let mut cache = FileContentHashCache::default();

        let first = cache
            .hash_file(
                &input,
                AssetHashStrategy::new(ContentHashAlgorithm::Blake3, 12),
            )
            .expect("first hash");
        fs::write(&input, "globalThis.x = 2;").expect("mutated input");
        let cached = cache
            .hash_file(
                &input,
                AssetHashStrategy::new(ContentHashAlgorithm::Blake3, 12),
            )
            .expect("cached hash");

        assert_eq!(first, cached);
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn budget_audit_detects_asset_and_route_violations() {
        let dir = unique_temp_dir("budget-audit");
        fs::create_dir_all(&dir).expect("temp dir");
        let html = dir.join("route.html");
        fs::write(
            &html,
            r#"<script type="application/json" id="__DXR_ROUTE_RUNTIME__">{
                "version":1,
                "route":"/effects",
                "assetBudgets":[
                    {
                        "assetId":"theme.runtime",
                        "packageId":"theme",
                        "assetKind":"javascript",
                        "loadingPolicy":"lazy",
                        "estimatedBytes":900,
                        "measuredBytes":880,
                        "cacheVersion":"theme-v1",
                        "preloadPreference":"idle",
                        "routeCost":{"routeId":"/effects","costUnits":1},
                        "budgetThreshold":{"errorBytes":1200}
                    },
                    {
                        "assetId":"shader.hot",
                        "packageId":"shader",
                        "assetKind":"javascript",
                        "loadingPolicy":"eager",
                        "estimatedBytes":2600,
                        "measuredBytes":2500,
                        "cacheVersion":"shader-v1",
                        "preloadPreference":"module-preload",
                        "routeCost":{"routeId":"/effects","costUnits":3,"critical":true},
                        "budgetThreshold":{"warningBytes":1800,"errorBytes":2400}
                    }
                ]
            }</script>"#,
        )
        .expect("route html");

        let report =
            budget_audit_report(&dir, None, Some(3_000), SourceMapInclusion::Exclude, None)
                .expect("budget audit");
        let text = format_budget_audit_text(&report);

        assert_eq!(report.asset_count, 2);
        assert_eq!(report.routes.len(), 1);
        assert_eq!(report.routes[0].route_id, "/effects");
        assert_eq!(report.routes[0].total_bytes, 3_380);
        assert_eq!(report.routes[0].critical_asset_count, 1);
        assert_eq!(report.violations.len(), 2);
        assert!(report.has_error_violations());
        assert!(text.contains("ROUTE_COST route=/effects totalBytes=3380"));
        assert!(text.contains(
            "BUDGET_VIOLATION level=error route=/effects package=shader asset=shader.hot"
        ));
        assert!(text.contains(
            "BUDGET_VIOLATION level=error route=/effects package=__route__ asset=__route__"
        ));
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn budget_audit_sorts_route_and_asset_violations() {
        let dir = unique_temp_dir("budget-audit-sort");
        fs::create_dir_all(&dir).expect("temp dir");
        let json = dir.join("runtime.json");
        fs::write(
            &json,
            r#"{
                "route":"/effects",
                "assetBudgets":[
                    {
                        "assetId":"shader.hot",
                        "packageId":"shader",
                        "assetKind":"javascript",
                        "measuredBytes":2500,
                        "routeCost":{"routeId":"/effects","costUnits":3},
                        "budgetThreshold":{"errorBytes":2000}
                    }
                ]
            }"#,
        )
        .expect("route json");

        let report =
            budget_audit_report(&json, None, Some(2_000), SourceMapInclusion::Exclude, None)
                .expect("budget audit");

        assert_eq!(report.violations.len(), 2);
        assert_eq!(report.violations[0].package_id, "__route__");
        assert_eq!(report.violations[1].package_id, "shader");
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn budget_audit_falls_back_to_runtime_assets() {
        let dir = unique_temp_dir("budget-fallback");
        fs::create_dir_all(&dir).expect("temp dir");
        let json = dir.join("runtime.json");
        fs::write(
            &json,
            r#"{
                "v":1,
                "r":"/hover",
                "a":{
                    "hoverfx.runtime":{
                        "path":"/assets/dioprism-hoverfx.js?v=abc",
                        "measuredBytes":1500,
                        "preloadPolicy":"idle"
                    }
                },
                "pkg":{
                    "hoverfx":{
                        "packageCost":"medium",
                        "preloadPolicy":"idle"
                    }
                }
            }"#,
        )
        .expect("route json");

        let report =
            budget_audit_report(&json, Some(1_000), None, SourceMapInclusion::Exclude, None)
                .expect("budget audit");

        assert_eq!(report.asset_count, 1);
        assert_eq!(report.assets[0].package_id, "hoverfx");
        assert_eq!(report.assets[0].cache_version, "abc");
        assert!(!report.assets[0].cache_key.is_empty());
        assert_eq!(report.assets[0].route_cost.cost_units, 2);
        assert_eq!(report.routes[0].total_bytes, 1_500);
        assert_eq!(report.violations.len(), 1);
        assert_eq!(report.violations[0].asset_id, "hoverfx.runtime");
        let _ = fs::remove_dir_all(dir);
    }

    #[test]
    fn budget_audit_reports_baseline_regressions_and_json_lines() {
        let dir = unique_temp_dir("budget-baseline");
        fs::create_dir_all(&dir).expect("temp dir");
        let json = dir.join("runtime.json");
        let baseline = dir.join("baseline.json");
        fs::write(
            &json,
            r#"{
                "route":"/effects",
                "assetBudgets":[
                    {
                        "assetId":"shader.hot",
                        "packageId":"shader",
                        "assetKind":"javascript",
                        "measuredBytes":2500,
                        "routeCost":{"routeId":"/effects","costUnits":3}
                    }
                ]
            }"#,
        )
        .expect("route json");
        fs::write(&baseline, r#"{"/effects":2000}"#).expect("baseline");

        let report = budget_audit_report(
            &json,
            None,
            None,
            SourceMapInclusion::Exclude,
            Some(&baseline),
        )
        .expect("budget audit");
        let json_lines = format_budget_audit_json_lines(&report).expect("json-lines");

        assert_eq!(report.violations.len(), 1);
        assert_eq!(report.violations[0].asset_id, "__route_regression__");
        assert!(json_lines.contains("\"type\":\"routeCost\""));
        assert!(json_lines.contains("\"type\":\"budgetViolation\""));
        let _ = fs::remove_dir_all(dir);
    }

    fn unique_temp_dir(label: &str) -> PathBuf {
        let stamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_default()
            .as_nanos();
        env::temp_dir().join(format!("dxr-assets-{label}-{stamp}"))
    }
}
