use std::collections::BTreeMap;
use std::fs;
use std::path::{Component, Path, PathBuf};
use std::time::UNIX_EPOCH;

use config_source::{ConfigContentType, ConfigSchemaContribution, VirtualPath};
use manifest::{ProfileSource, resolve_profile_artifact_value};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use worker::EffectivePromptCatalog;
use worker_runtime::config_bundle::{
    ConfigBundle, ConfigBundleMetadata, ConfigBundleProvenance, ConfigProfileDescriptor,
};
use worker_runtime::profile_archive::{ProfileSourceArchive, ProfileSourceArchiveInput};

use crate::config_source::{
    WorkspaceConfigSchemaProvider, WorkspaceConfigState, evaluate_workspace_config_state,
};
use crate::hosts::{DiagnosticSeverity, RuntimeDiagnostic};
use crate::{Error, Result};

const PROFILE_SCHEMA_SOURCE: &str = r#"{
    profile = {
        default_profile = String default "builtin:companion";
        entries = [...{
            selector = String;
            source = String;
            label = String default "";
            description = String default "";
        }] default [];
    };
}"#;

#[derive(Debug, Default)]
pub struct ProfileConfigSchemaProvider;

impl WorkspaceConfigSchemaProvider for ProfileConfigSchemaProvider {
    fn contribution(&self) -> Result<ConfigSchemaContribution> {
        ConfigSchemaContribution::new("builtin:profile", "profile", "1", PROFILE_SCHEMA_SOURCE)
            .map_err(|error| Error::Config(error.to_string()))
    }
}

#[derive(Debug, Clone, Deserialize)]
struct VirtualProfileConfig {
    profile: VirtualProfileSection,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
struct VirtualProfileSection {
    default_profile: String,
    entries: Vec<VirtualProfileEntry>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
struct VirtualProfileEntry {
    selector: String,
    source: String,
    label: String,
    description: String,
}

#[derive(Debug, Clone)]
pub struct ProfileConfigProjection {
    pub settings: ProfileSettingsResponse,
    entries: BTreeMap<String, VirtualProfileEntry>,
    sources: BTreeMap<String, String>,
}

pub fn project_profiles_from_workspace_config(
    workspace_id: &str,
    state: &WorkspaceConfigState,
) -> Result<ProfileConfigProjection> {
    let bundle = if state.contract.schema_bundle.contributions.is_empty() {
        config_source::WorkspaceConfigSchemaBundle::compose([
            ProfileConfigSchemaProvider.contribution()?
        ])
        .map_err(|error| Error::Config(error.to_string()))?
    } else {
        state.contract.schema_bundle.clone()
    };
    let evaluation = evaluate_workspace_config_state(state, bundle)?;
    if evaluation.projection_digest != state.projection_digest
        && state
            .contract
            .schema_bundle
            .contributions
            .iter()
            .any(|entry| entry.provider_id == "builtin:profile")
    {
        return Err(Error::RegistryInconsistency(format!(
            "Profile projection digest mismatch for Workspace {workspace_id}"
        )));
    }
    let projected = evaluation.projections.first().ok_or_else(|| {
        Error::RegistryInconsistency("Workspace config has no active projection".to_string())
    })?;
    let config: VirtualProfileConfig = serde_json::from_value(projected.data_json.clone())
        .map_err(|error| Error::RegistryInconsistency(error.to_string()))?;
    let mut profiles = builtin_profile_summaries(Some(&config.profile.default_profile));
    let mut entries = BTreeMap::new();
    let sources = state
        .snapshot
        .entries
        .iter()
        .filter(|(_, entry)| entry.content_type == ConfigContentType::Decodal)
        .map(|(path, entry)| (path.as_str().to_string(), entry.content.clone()))
        .collect::<BTreeMap<_, _>>();
    let mut source_summaries = Vec::new();
    for entry in config.profile.entries {
        if !entry.selector.starts_with("project:") {
            return Err(profile_validation_error(
                "profile_selector_invalid",
                "Workspace Profile selectors must use project:*",
            ));
        }
        if entries.contains_key(&entry.selector) {
            return Err(profile_validation_error(
                "profile_selector_duplicate",
                "Workspace Profile selectors must be unique",
            ));
        }
        let source_path = VirtualPath::parse(&entry.source).map_err(|error| {
            profile_validation_error("profile_source_path_invalid", &error.to_string())
        })?;
        let source_entry = state.snapshot.get(&source_path).ok_or_else(|| {
            profile_validation_error(
                "profile_source_missing",
                &format!(
                    "Profile source {:?} is missing from the active config revision",
                    entry.source
                ),
            )
        })?;
        if source_entry.content_type != ConfigContentType::Decodal {
            return Err(profile_validation_error(
                "profile_source_type_invalid",
                "Profile sources must use Decodal content",
            ));
        }
        let archive_source = entry.source.clone();
        let label = if entry.label.is_empty() {
            entry.selector.trim_start_matches("project:").to_string()
        } else {
            entry.label.clone()
        };
        resolve_profile_artifact_value(
            evaluate_profile_source(&state.snapshot, &source_path)?,
            ProfileSource::Archive {
                archive_id: format!("workspace-config-r{}", state.snapshot.revision),
                source: archive_source.clone(),
            },
            Path::new("/"),
            "workspace-config-validation",
        )
        .map_err(|error| profile_validation_error("profile_source_invalid", &error.to_string()))?;
        profiles.push(WorkspaceProfileSummary {
            profile_id: entry.selector.clone(),
            selector: entry.selector.clone(),
            label,
            source_kind: "project".to_string(),
            profile_source_id: Some(entry.source.clone()),
            description: (!entry.description.is_empty()).then(|| entry.description.clone()),
            editable: false,
            is_default: config.profile.default_profile == entry.selector,
            diagnostics: Vec::new(),
        });
        source_summaries.push(WorkspaceProfileSourceSummary {
            profile_source_id: entry.source.clone(),
            display_path: entry.source.clone(),
            kind: "virtual_config".to_string(),
            content_type: "decodal".to_string(),
            content_digest: source_entry.content_digest.clone(),
            provenance: WorkspaceProfileSourceProvenance::ProjectProfileSourceTree,
            editable: false,
            revision: state.snapshot.revision.to_string(),
            size_bytes: source_entry.content.len() as u64,
            diagnostics: Vec::new(),
        });
        entries.insert(entry.selector.clone(), entry);
    }
    if !profiles
        .iter()
        .any(|profile| profile.selector == config.profile.default_profile)
    {
        return Err(profile_validation_error(
            "unknown_default_profile",
            "Default Profile must select a builtin or Workspace Profile",
        ));
    }
    Ok(ProfileConfigProjection {
        settings: ProfileSettingsResponse {
            workspace_id: workspace_id.to_string(),
            registry_revision: format!("config:{}", state.snapshot.revision),
            config_revision: Some(state.snapshot.revision),
            tree_digest: Some(state.snapshot.digest.clone()),
            projection_digest: Some(evaluation.projection_digest),
            default_profile: Some(config.profile.default_profile),
            profiles,
            sources: source_summaries,
            diagnostics: Vec::new(),
        },
        entries,
        sources,
    })
}

fn evaluate_profile_source(
    snapshot: &config_source::ConfigTreeSnapshot,
    source_path: &VirtualPath,
) -> Result<serde_json::Value> {
    let contract = config_source::ToolchainContract::with_schema_bundle(
        config_source::DEFAULT_SCHEMA_VERSION,
        vec![source_path.clone()],
        config_source::DEFAULT_IMPORT_POLICY_VERSION,
        config_source::WorkspaceConfigSchemaBundle::empty(),
    );
    let evaluation = config_source::SnapshotEnvironment::new(snapshot.clone())
        .evaluate_contract(&contract)
        .map_err(|diagnostics| {
            profile_validation_error(
                "profile_source_invalid",
                &serde_json::to_string(&diagnostics)
                    .unwrap_or_else(|_| "Profile source evaluation failed".to_string()),
            )
        })?;
    evaluation
        .projections
        .into_iter()
        .next()
        .map(|projection| projection.data_json)
        .ok_or_else(|| {
            profile_validation_error("profile_source_invalid", "Profile source has no projection")
        })
}

pub fn selector_for_workspace_candidate(
    projection: &ProfileConfigProjection,
    profile: &str,
) -> Option<worker_runtime::catalog::ProfileSelector> {
    if let Some(selector) = selector_for_builtin_candidate(profile) {
        return Some(selector);
    }
    projection
        .entries
        .contains_key(profile)
        .then(|| worker_runtime::catalog::ProfileSelector::Named(profile.to_string()))
}

fn validate_prompt_projection_matches_state(
    workspace_id: &str,
    state: &WorkspaceConfigState,
    projection: &worker::WorkspacePromptProjection,
) -> Result<()> {
    projection
        .validate()
        .map_err(|error| Error::Config(error.to_string()))?;
    let prompt_catalog = projection.catalog();
    let mismatches = [
        (
            projection.workspace_id != workspace_id,
            "workspace identity",
        ),
        (
            projection.source_digest != state.snapshot.digest,
            "source digest",
        ),
        (
            projection.projection_digest != prompt_catalog.catalog_digest,
            "projection digest",
        ),
        (
            prompt_catalog.config_revision != state.snapshot.revision,
            "config revision",
        ),
        (
            prompt_catalog.schema_fingerprint != state.contract.schema_bundle.fingerprint,
            "schema fingerprint",
        ),
        (
            prompt_catalog.toolchain_fingerprint != state.contract.fingerprint,
            "toolchain fingerprint",
        ),
    ]
    .into_iter()
    .filter_map(|(mismatch, label)| mismatch.then_some(label))
    .collect::<Vec<_>>();
    if mismatches.is_empty() {
        Ok(())
    } else {
        Err(Error::Config(format!(
            "Prompt projection does not match Workspace config state: {}",
            mismatches.join(", ")
        )))
    }
}

fn virtual_profile_bundle_id(
    state: &WorkspaceConfigState,
    workspace_id: &str,
    profile_selector: &worker_runtime::catalog::ProfileSelector,
    prompt_catalog: &EffectivePromptCatalog,
    archive: Option<&ProfileSourceArchive>,
) -> Result<String> {
    let mut hasher = Sha256::new();
    hasher.update(b"workspace-profile-launch-v1\0");
    hasher.update(workspace_id.as_bytes());
    hasher.update(b"\0");
    hasher.update(state.snapshot.revision.to_le_bytes());
    hasher.update(b"\0");
    hasher.update(state.snapshot.digest.as_bytes());
    hasher.update(b"\0");
    hasher.update(state.projection_digest.as_bytes());
    hasher.update(b"\0");
    hasher.update(
        serde_json::to_vec(profile_selector).map_err(|error| Error::Config(error.to_string()))?,
    );
    hasher.update(b"\0");
    hasher.update(prompt_catalog.catalog_digest.as_bytes());
    hasher.update(b"\0");
    hasher.update(prompt_catalog.schema_fingerprint.as_bytes());
    hasher.update(b"\0");
    hasher.update(prompt_catalog.toolchain_fingerprint.as_bytes());
    if let Some(archive) = archive {
        hasher.update(b"\0");
        hasher.update(archive.reference.digest.as_bytes());
    }
    let digest = hasher.finalize();
    let identity = digest
        .iter()
        .map(|byte| format!("{byte:02x}"))
        .collect::<String>();
    Ok(format!(
        "workspace-config-profile-r{}-{identity}",
        state.snapshot.revision
    ))
}

pub fn build_virtual_profile_config_bundle(
    projection: &ProfileConfigProjection,
    state: &WorkspaceConfigState,
    workspace_id: &str,
    workspace_created_at: &str,
    selector: &str,
) -> Result<Option<ConfigBundle>> {
    let prompt_projection =
        crate::prompt_settings::project_workspace_prompt_projection(workspace_id, state)?;
    build_virtual_profile_config_bundle_with_prompt_projection(
        projection,
        state,
        workspace_id,
        workspace_created_at,
        selector,
        &prompt_projection,
    )
}

pub fn build_virtual_profile_config_bundle_with_prompt_projection(
    projection: &ProfileConfigProjection,
    state: &WorkspaceConfigState,
    workspace_id: &str,
    workspace_created_at: &str,
    selector: &str,
    prompt_projection: &worker::WorkspacePromptProjection,
) -> Result<Option<ConfigBundle>> {
    validate_prompt_projection_matches_state(workspace_id, state, prompt_projection)?;
    let prompt_catalog = prompt_projection.catalog().clone();
    let archive = projection
        .entries
        .get(selector)
        .map(|entry| build_virtual_profile_archive(selector, entry, &projection.sources, state))
        .transpose()?;
    let profile_selector = selector_for_builtin_candidate(selector)
        .unwrap_or_else(|| worker_runtime::catalog::ProfileSelector::Named(selector.to_string()));
    let bundle_id = virtual_profile_bundle_id(
        state,
        workspace_id,
        &profile_selector,
        &prompt_catalog,
        archive.as_ref(),
    )?;
    let bundle = ConfigBundle {
        metadata: ConfigBundleMetadata {
            id: bundle_id,
            digest: String::new(),
            revision: state.snapshot.revision.to_string(),
            workspace_id: workspace_id.to_string(),
            created_at: workspace_created_at.to_string(),
            provenance: ConfigBundleProvenance {
                source: "workspace_config".to_string(),
                detail: Some(format!(
                    "revision={} tree={} projection={}",
                    state.snapshot.revision, state.snapshot.digest, state.projection_digest
                )),
            },
        },
        profiles: vec![ConfigProfileDescriptor {
            selector: profile_selector,
            label: Some(selector.to_string()),
        }],
        declarations: Vec::new(),
        prompt_catalog: Some(prompt_catalog),
        profile_source_archive: archive,
        profile_source_archive_handle: None,
    }
    .with_computed_digest();
    Ok(Some(bundle))
}

fn build_virtual_profile_archive(
    selector: &str,
    entry: &VirtualProfileEntry,
    all_sources: &BTreeMap<String, String>,
    state: &WorkspaceConfigState,
) -> Result<ProfileSourceArchive> {
    let mut closure = BTreeMap::new();
    let mut imports = BTreeMap::new();
    collect_profile_import_closure(&entry.source, all_sources, &mut closure, &mut imports)?;
    ProfileSourceArchive::build(ProfileSourceArchiveInput {
        id: format!("workspace-config-profile-r{}", state.snapshot.revision),
        entrypoints: BTreeMap::from([(selector.to_string(), entry.source.clone())]),
        imports,
        sources: closure,
    })
    .map_err(|error| profile_validation_error("profile_source_archive_invalid", &error.to_string()))
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceMetadataSettingsResponse {
    pub workspace_id: String,
    pub display_name: String,
    pub created_at: String,
    pub revision: String,
    pub source: String,
    pub diagnostics: Vec<RuntimeDiagnostic>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UpdateWorkspaceMetadataRequest {
    pub display_name: String,
    pub revision: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceMetadataMutationResponse {
    pub workspace: WorkspaceMetadataSettingsResponse,
    pub diagnostics: Vec<RuntimeDiagnostic>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProfileSettingsResponse {
    pub workspace_id: String,
    pub registry_revision: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub config_revision: Option<u64>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub tree_digest: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub projection_digest: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub default_profile: Option<String>,
    pub profiles: Vec<WorkspaceProfileSummary>,
    pub sources: Vec<WorkspaceProfileSourceSummary>,
    pub diagnostics: Vec<RuntimeDiagnostic>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceProfileSummary {
    pub profile_id: String,
    pub selector: String,
    pub label: String,
    pub source_kind: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub profile_source_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    pub editable: bool,
    pub is_default: bool,
    pub diagnostics: Vec<RuntimeDiagnostic>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceProfileSourceSummary {
    pub profile_source_id: String,
    pub display_path: String,
    pub kind: String,
    pub content_type: String,
    pub content_digest: String,
    pub provenance: WorkspaceProfileSourceProvenance,
    pub editable: bool,
    pub revision: String,
    pub size_bytes: u64,
    pub diagnostics: Vec<RuntimeDiagnostic>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WorkspaceProfileSourceProvenance {
    ProjectProfileSourceTree,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
struct WorkspaceIdentityFile {
    workspace_id: String,
    created_at: String,
    display_name: String,
}

pub fn workspace_metadata_settings(
    workspace_root: &Path,
    fallback_workspace_id: &str,
    fallback_created_at: &str,
    fallback_display_name: &str,
) -> WorkspaceMetadataSettingsResponse {
    let path = workspace_root.join(crate::identity::WORKSPACE_IDENTITY_RELATIVE_PATH);
    let mut diagnostics = Vec::new();
    let (workspace_id, created_at, display_name) = match fs::read_to_string(&path) {
        Ok(raw) => match toml::from_str::<WorkspaceIdentityFile>(&raw) {
            Ok(file) => (file.workspace_id, file.created_at, file.display_name),
            Err(err) => {
                diagnostics.push(diagnostic(
                    "workspace_identity_parse_failed",
                    DiagnosticSeverity::Error,
                    format!("Workspace identity could not be parsed: {err}"),
                ));
                (
                    fallback_workspace_id.to_string(),
                    fallback_created_at.to_string(),
                    fallback_display_name.to_string(),
                )
            }
        },
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
            diagnostics.push(diagnostic(
                "workspace_identity_missing",
                DiagnosticSeverity::Warning,
                "Workspace identity record is missing; showing active backend metadata.",
            ));
            (
                fallback_workspace_id.to_string(),
                fallback_created_at.to_string(),
                fallback_display_name.to_string(),
            )
        }
        Err(err) => {
            diagnostics.push(diagnostic(
                "workspace_identity_read_failed",
                DiagnosticSeverity::Error,
                format!(
                    "Workspace identity could not be read: {}",
                    sanitize_error(&err.to_string())
                ),
            ));
            (
                fallback_workspace_id.to_string(),
                fallback_created_at.to_string(),
                fallback_display_name.to_string(),
            )
        }
    };
    WorkspaceMetadataSettingsResponse {
        workspace_id,
        display_name,
        created_at,
        revision: file_revision(&path),
        source: "workspace_identity".to_string(),
        diagnostics,
    }
}

pub fn update_workspace_metadata(
    workspace_root: &Path,
    request: UpdateWorkspaceMetadataRequest,
) -> Result<WorkspaceMetadataSettingsResponse> {
    let path = workspace_root.join(crate::identity::WORKSPACE_IDENTITY_RELATIVE_PATH);
    let current_revision = file_revision(&path);
    if request.revision != current_revision {
        return Err(Error::RuntimeOperationFailed {
            runtime_id: "workspace-backend".to_string(),
            code: "workspace_metadata_revision_conflict".to_string(),
            message: "Workspace metadata changed before this update was applied".to_string(),
        });
    }
    let raw = fs::read_to_string(&path)?;
    let mut file: WorkspaceIdentityFile = toml::from_str(&raw)
        .map_err(|err| Error::Config(format!("failed to parse workspace identity: {err}")))?;
    let display_name = sanitize_display_name(&request.display_name)?;
    file.display_name = display_name;
    let encoded = toml::to_string_pretty(&file)
        .map_err(|err| Error::Config(format!("failed to serialize workspace identity: {err}")))?;
    fs::write(&path, encoded)?;
    Ok(workspace_metadata_settings(
        workspace_root,
        &file.workspace_id,
        &file.created_at,
        &file.display_name,
    ))
}

fn builtin_profile_summaries(default_profile: Option<&str>) -> Vec<WorkspaceProfileSummary> {
    let labels = [
        (
            "builtin:companion",
            "Companion",
            "Bundled Companion role profile",
        ),
        ("builtin:intake", "Intake", "Bundled Intake role profile"),
        (
            "builtin:orchestrator",
            "Orchestrator",
            "Bundled Orchestrator role profile",
        ),
        ("builtin:coder", "Coder", "Bundled Coder role profile"),
        (
            "builtin:reviewer",
            "Reviewer",
            "Bundled Reviewer role profile",
        ),
    ];
    labels
        .into_iter()
        .map(|(id, label, description)| WorkspaceProfileSummary {
            profile_id: id.to_string(),
            selector: id.to_string(),
            label: label.to_string(),
            source_kind: "builtin".to_string(),
            profile_source_id: None,
            description: Some(description.to_string()),
            editable: false,
            is_default: default_profile == Some(id),
            diagnostics: Vec::new(),
        })
        .collect()
}

fn profile_validation_error(code: impl Into<String>, message: impl Into<String>) -> Error {
    Error::RuntimeOperationFailed {
        runtime_id: "workspace-backend".to_string(),
        code: code.into(),
        message: message.into(),
    }
}

fn collect_profile_import_closure(
    current_path: &str,
    all_sources: &BTreeMap<String, String>,
    closure_sources: &mut BTreeMap<String, String>,
    imports: &mut BTreeMap<String, String>,
) -> Result<()> {
    if closure_sources.contains_key(current_path) {
        return Ok(());
    }
    let content = all_sources.get(current_path).ok_or_else(|| {
        profile_validation_error(
            "profile_source_import_missing",
            &format!("Profile source import closure is missing {current_path}"),
        )
    })?;
    closure_sources.insert(current_path.to_string(), content.clone());
    for specifier in collect_decodal_import_specifiers(content) {
        let target = resolve_profile_source_import(current_path, &specifier)?;
        if !all_sources.contains_key(&target) {
            return Err(profile_validation_error(
                "profile_source_import_missing",
                &format!(
                    "Profile source import {specifier:?} from {current_path} resolves to missing {target}"
                ),
            ));
        }
        imports.insert(format!("{current_path}\0{specifier}"), target.clone());
        collect_profile_import_closure(&target, all_sources, closure_sources, imports)?;
    }
    Ok(())
}

fn resolve_profile_source_import(current_path: &str, specifier: &str) -> Result<String> {
    if specifier.is_empty() || specifier.contains("://") || Path::new(specifier).is_absolute() {
        return Err(profile_validation_error(
            "profile_source_import_invalid",
            "Profile source import must be a virtual relative path",
        ));
    }
    let raw = specifier
        .strip_prefix("project:")
        .or_else(|| specifier.strip_prefix("workspace:"))
        .unwrap_or(specifier);
    if specifier.contains(':') && raw == specifier {
        return Err(profile_validation_error(
            "profile_source_import_invalid",
            "Unsupported profile source import namespace",
        ));
    }
    let base = if raw.starts_with("profiles/") || raw.starts_with("./profiles/") {
        PathBuf::from(raw)
    } else {
        Path::new(current_path)
            .parent()
            .map(Path::to_path_buf)
            .unwrap_or_default()
            .join(raw)
    };
    let normalized = normalize_virtual_profile_source_path(&base.to_string_lossy())?;
    Ok(normalized)
}

fn normalize_virtual_profile_source_path(path: &str) -> Result<String> {
    let mut normalized = PathBuf::new();
    for component in Path::new(path).components() {
        match component {
            Component::CurDir => {}
            Component::Normal(value) => normalized.push(value),
            Component::ParentDir => {
                if !normalized.pop() {
                    return Err(profile_validation_error(
                        "profile_source_import_invalid",
                        "Profile source import escapes the virtual tree",
                    ));
                }
            }
            Component::RootDir | Component::Prefix(_) => {
                return Err(profile_validation_error(
                    "profile_source_import_invalid",
                    "Profile source import must be relative",
                ));
            }
        }
    }
    Ok(normalized.to_string_lossy().replace('\\', "/"))
}

fn collect_decodal_import_specifiers(content: &str) -> Vec<String> {
    let mut specifiers = Vec::new();
    for line in content.lines() {
        let trimmed = line.trim_start();
        if trimmed.starts_with("//") || trimmed.starts_with('#') {
            continue;
        }
        let Some(index) = trimmed.find("import") else {
            continue;
        };
        let after = trimmed[index + "import".len()..].trim_start();
        let Some(first) = after.chars().next() else {
            continue;
        };
        if first == '"' || first == '\'' {
            if let Some(end) = after[1..].find(first) {
                specifiers.push(after[1..1 + end].to_string());
            }
        } else {
            let ident: String = after
                .chars()
                .take_while(|ch| !ch.is_whitespace() && *ch != ';' && *ch != ',' && *ch != '{')
                .collect();
            if !ident.is_empty() {
                specifiers.push(ident);
            }
        }
    }
    specifiers
}

fn sanitize_display_name(value: &str) -> Result<String> {
    let trimmed = value.trim();
    if trimmed.is_empty() || trimmed.chars().any(char::is_control) || trimmed.len() > 120 {
        return Err(Error::RuntimeOperationFailed {
            runtime_id: "workspace-backend".to_string(),
            code: "workspace_display_name_invalid".to_string(),
            message: "Workspace display name must be non-empty, bounded, and must not contain control characters".to_string(),
        });
    }
    Ok(trimmed.to_string())
}
pub fn selector_for_builtin_candidate(
    id: &str,
) -> Option<worker_runtime::catalog::ProfileSelector> {
    match id {
        "builtin:companion"
        | "builtin:intake"
        | "builtin:orchestrator"
        | "builtin:coder"
        | "builtin:reviewer" => Some(worker_runtime::catalog::ProfileSelector::Builtin(
            id.to_string(),
        )),
        _ => None,
    }
}
fn file_revision(path: &Path) -> String {
    let Ok(metadata) = fs::metadata(path) else {
        return "missing".to_string();
    };
    let modified = metadata
        .modified()
        .ok()
        .and_then(|time| time.duration_since(UNIX_EPOCH).ok())
        .map(|duration| duration.as_nanos())
        .unwrap_or_default();
    format!("rev:{modified}:{}", metadata.len())
}
fn diagnostic(
    code: impl Into<String>,
    severity: DiagnosticSeverity,
    message: impl Into<String>,
) -> RuntimeDiagnostic {
    RuntimeDiagnostic {
        code: code.into(),
        severity,
        message: message.into(),
    }
}
fn sanitize_error(value: &str) -> String {
    value
        .split_whitespace()
        .map(|token| {
            if token.starts_with('/') || token.contains("/.yoi/") || token.contains(".yoi/sessions")
            {
                "<redacted-path>"
            } else {
                token
            }
        })
        .collect::<Vec<_>>()
        .join(" ")
}

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

    fn valid_decodal(slug: &str) -> String {
        format!(r#"{{ slug = "{slug}"; model = {{ id = "gpt-5.4"; }}; }}"#)
    }

    fn virtual_state(entries: Vec<config_source::ConfigEntry>) -> WorkspaceConfigState {
        let snapshot = config_source::ConfigTreeSnapshot::from_entries(7, entries).unwrap();
        let schema_bundle = config_source::WorkspaceConfigSchemaBundle::compose([
            ProfileConfigSchemaProvider.contribution().unwrap(),
            crate::prompt_settings::PromptConfigSchemaProvider
                .contribution()
                .unwrap(),
        ])
        .unwrap();
        let contract = config_source::ToolchainContract::with_schema_bundle(
            config_source::DEFAULT_SCHEMA_VERSION,
            vec![VirtualPath::parse("main.dcdl").unwrap()],
            config_source::DEFAULT_IMPORT_POLICY_VERSION,
            schema_bundle,
        );
        let projection_digest = config_source::SnapshotEnvironment::new(snapshot.clone())
            .evaluate_contract(&contract)
            .unwrap()
            .projection_digest;
        WorkspaceConfigState {
            projection_digest,
            contract,
            snapshot,
        }
    }

    #[test]
    fn virtual_config_projection_builds_archive_from_active_revision() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                r#"{ profile = { default_profile = "project:alpha"; entries = [{ selector = "project:alpha"; source = "profiles/alpha.dcdl"; label = "Alpha"; }]; }; }"#,
            )
            .unwrap(),
            config_source::ConfigEntry::new(
                VirtualPath::parse("profiles/alpha.dcdl").unwrap(),
                ConfigContentType::Decodal,
                valid_decodal("alpha"),
            )
            .unwrap(),
        ]);
        let projection = project_profiles_from_workspace_config("workspace-test", &state).unwrap();
        let bundle = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "project:alpha",
        )
        .unwrap()
        .unwrap();
        assert_eq!(projection.settings.config_revision, Some(7));
        let prompt_catalog = bundle.prompt_catalog.as_ref().unwrap();
        assert_eq!(prompt_catalog.config_revision, 7);
        assert!(!prompt_catalog.templates.is_empty());
        assert!(
            bundle
                .metadata
                .provenance
                .detail
                .unwrap()
                .contains("revision=7")
        );
        let archive = bundle.profile_source_archive.unwrap();
        assert_eq!(
            archive
                .reference
                .source_graph
                .entrypoints
                .get("project:alpha")
                .map(String::as_str),
            Some("profiles/alpha.dcdl")
        );
        assert_eq!(archive.reference.source_graph.source_count, 1);
    }

    #[test]
    fn virtual_config_projection_preserves_import_closure() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                r#"{ profile = { entries = [{ selector = "project:alpha"; source = "profiles/alpha.dcdl"; }]; }; }"#,
            )
            .unwrap(),
            config_source::ConfigEntry::new(
                VirtualPath::parse("profiles/alpha.dcdl").unwrap(),
                ConfigContentType::Decodal,
                r#"import "../shared/profile.dcdl""#,
            )
            .unwrap(),
            config_source::ConfigEntry::new(
                VirtualPath::parse("shared/profile.dcdl").unwrap(),
                ConfigContentType::Decodal,
                valid_decodal("alpha"),
            )
            .unwrap(),
        ]);
        let projection = project_profiles_from_workspace_config("workspace-test", &state).unwrap();
        let bundle = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "project:alpha",
        )
        .unwrap()
        .unwrap();
        let archive = bundle.profile_source_archive.unwrap();
        assert_eq!(archive.reference.source_graph.source_count, 2);
        assert_eq!(archive.reference.source_graph.import_count, 1);
    }

    #[test]
    fn virtual_config_launch_bundle_identity_is_profile_specific_and_stable() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                "{}",
            )
            .unwrap(),
        ]);
        let projection = project_profiles_from_workspace_config("workspace-test", &state).unwrap();

        let companion = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "builtin:companion",
        )
        .unwrap()
        .unwrap();
        let companion_retry = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "builtin:companion",
        )
        .unwrap()
        .unwrap();
        let coder = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "builtin:coder",
        )
        .unwrap()
        .unwrap();

        assert_eq!(companion.metadata.id, companion_retry.metadata.id);
        assert_eq!(companion.metadata.digest, companion_retry.metadata.digest);
        assert_ne!(companion.metadata.id, coder.metadata.id);
        assert_ne!(companion.metadata.digest, coder.metadata.digest);
        assert!(
            companion
                .metadata
                .id
                .starts_with("workspace-config-profile-r7-")
        );
        assert!(
            coder
                .metadata
                .id
                .starts_with("workspace-config-profile-r7-")
        );
    }

    #[test]
    fn virtual_config_launch_bundle_rejects_prompt_projection_from_other_revision() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                "{}",
            )
            .unwrap(),
        ]);
        let projection = project_profiles_from_workspace_config("workspace-test", &state).unwrap();
        let prompt_projection =
            crate::prompt_settings::project_workspace_prompt_projection("workspace-test", &state)
                .unwrap();
        let mut mismatched_state = state.clone();
        mismatched_state.snapshot.revision += 1;

        let error = build_virtual_profile_config_bundle_with_prompt_projection(
            &projection,
            &mismatched_state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "builtin:coder",
            &prompt_projection,
        )
        .unwrap_err();
        assert!(error.to_string().contains("does not match"));
    }

    #[test]
    fn virtual_config_launch_bundle_identity_separates_project_profile_archives() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                r#"{ profile = { entries = [
                    { selector = "project:alpha"; source = "profiles/alpha.dcdl"; },
                    { selector = "project:beta"; source = "profiles/beta.dcdl"; },
                ]; }; }"#,
            )
            .unwrap(),
            config_source::ConfigEntry::new(
                VirtualPath::parse("profiles/alpha.dcdl").unwrap(),
                ConfigContentType::Decodal,
                valid_decodal("alpha"),
            )
            .unwrap(),
            config_source::ConfigEntry::new(
                VirtualPath::parse("profiles/beta.dcdl").unwrap(),
                ConfigContentType::Decodal,
                valid_decodal("beta"),
            )
            .unwrap(),
        ]);
        let projection = project_profiles_from_workspace_config("workspace-test", &state).unwrap();
        let alpha = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "project:alpha",
        )
        .unwrap()
        .unwrap();
        let beta = build_virtual_profile_config_bundle(
            &projection,
            &state,
            "workspace-test",
            "2026-01-01T00:00:00Z",
            "project:beta",
        )
        .unwrap()
        .unwrap();

        assert_ne!(alpha.metadata.id, beta.metadata.id);
        assert_ne!(alpha.metadata.digest, beta.metadata.digest);
        assert_ne!(
            alpha.profile_source_archive.unwrap().reference.digest,
            beta.profile_source_archive.unwrap().reference.digest
        );
    }

    #[test]
    fn virtual_config_projection_rejects_missing_profile_source() {
        let state = virtual_state(vec![
            config_source::ConfigEntry::new(
                VirtualPath::parse("main.dcdl").unwrap(),
                ConfigContentType::Decodal,
                r#"{ profile = { entries = [{ selector = "project:alpha"; source = "profiles/missing.dcdl"; }]; }; }"#,
            )
            .unwrap(),
        ]);
        let error = project_profiles_from_workspace_config("workspace-test", &state).unwrap_err();
        assert!(
            error
                .to_string()
                .contains("missing from the active config revision")
        );
    }
}
