//! Emit Swift class declarations for opaque handle types marked with
//! `#[swift_bridge(already_declared)]` in the Rust extern blocks.
//!
//! When swift-bridge sees `#[swift_bridge(already_declared)]` on a type declaration,
//! it skips generating the corresponding Swift class wrapper (to avoid duplicate
//! `__swift_bridge__{Type}__free` symbols). Alef must then provide the Swift class
//! hierarchy. Currently, this only applies to owner types of streaming adapters,
//! which are forward-declared in the streaming extern block.

use crate::core::backend::GeneratedFile;
use crate::core::config::{AdapterPattern, ResolvedCrateConfig};
use std::path::Path;

/// Collect all unique streaming adapter owner types that require a Swift class
/// declaration because they are marked with `#[swift_bridge(already_declared)]`.
///
/// Exposed `pub(crate)` so the Rust-glue phantom-Vec emitter can SKIP these
/// types — `Vec<AlreadyDeclaredType>` cannot be constructed in Swift because
/// swift-bridge will not synthesize a `Vectorizable` conformance for the type
/// (the `already_declared` marker exists precisely to suppress that synthesis).
pub(crate) fn collect_already_declared_owner_types(config: &ResolvedCrateConfig) -> std::collections::BTreeSet<String> {
    let mut owner_types = std::collections::BTreeSet::new();
    for adapter in &config.adapters {
        if matches!(adapter.pattern, AdapterPattern::Streaming) {
            if let Some(owner) = adapter.owner_type.as_deref() {
                owner_types.insert(owner.to_string());
            }
        }
    }
    owner_types
}

/// Emit a Swift file declaring all opaque handle class triples for types marked
/// with `#[swift_bridge(already_declared)]` in the Rust extern blocks.
///
/// Returns `None` if there are no such types.
pub(crate) fn emit_opaque_class_declarations(
    config: &ResolvedCrateConfig,
    rust_bridge_sources: &Path,
) -> Option<GeneratedFile> {
    let owner_types = collect_already_declared_owner_types(config);
    if owner_types.is_empty() {
        return None;
    }

    // Streaming-adapter owner handles (e.g. `CrawlEngineHandle`) are now declared
    // exactly once, canonically, in the Rust functions extern block — co-located
    // with their by-value `*_new`/`create_*` return. swift-bridge therefore
    // synthesizes the full `{Type}` / `{Type}RefMut` / `{Type}Ref` class triple and
    // the `$_free` destructor itself. Emitting the triple here as well caused
    // `invalid redeclaration` / `ambiguous for type lookup` Swift errors. We keep
    // generating the file (header only) so any previously-generated triple is
    // overwritten on regen, and so `collect_already_declared_owner_types` still
    // drives the Rust-glue phantom-Vec skip (swift-bridge owns the Vectorizable
    // conformance for these canonical types).
    let mut content = String::new();
    content.push_str("// Generated by alef — intentionally empty.\n");
    content.push_str("// Streaming-adapter owner handles are declared canonically (once) in the\n");
    content.push_str("// Rust extern blocks, so swift-bridge synthesizes their class triple and\n");
    content.push_str("// `$_free` destructor directly; no hand-rolled class hierarchy is needed.\n\n");
    content.push_str("import Foundation\n");
    content.push_str("import RustBridgeC\n");

    let path = rust_bridge_sources.join("RustBridgeOpaqueHandles.swift");
    Some(GeneratedFile {
        path,
        content,
        generated_header: false,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::core::config::{AdapterConfig, AdapterParam, AdapterPattern};

    fn streaming_adapter_with_owner(name: &str, owner: &str) -> AdapterConfig {
        AdapterConfig {
            name: name.to_string(),
            pattern: AdapterPattern::Streaming,
            core_path: format!("sample::{name}"),
            params: vec![AdapterParam {
                name: "req".to_string(),
                ty: "sample::StreamRequest".to_string(),
                optional: false,
            }],
            returns: None,
            error_type: Some("String".to_string()),
            owner_type: Some(owner.to_string()),
            item_type: Some("StreamItem".to_string()),
            gil_release: false,
            trait_name: None,
            trait_method: None,
            detect_async: false,
            request_type: Some("sample::StreamRequest".to_string()),
            skip_languages: vec![],
        }
    }

    #[test]
    fn collect_already_declared_owner_types_deduplicates() {
        let config = ResolvedCrateConfig {
            adapters: vec![
                streaming_adapter_with_owner("chat_stream", "DefaultClient"),
                streaming_adapter_with_owner("batch_stream", "DefaultClient"),
                streaming_adapter_with_owner("crawl_stream", "CrawlEngine"),
            ],
            ..Default::default()
        };

        let owners = collect_already_declared_owner_types(&config);
        assert_eq!(owners.len(), 2);
        assert!(owners.contains("DefaultClient"));
        assert!(owners.contains("CrawlEngine"));
    }

    #[test]
    fn collect_already_declared_owner_types_skips_non_streaming() {
        // Only create a streaming adapter; it will be the only one with owner_type
        let config = ResolvedCrateConfig {
            adapters: vec![streaming_adapter_with_owner("stream", "StreamClient")],
            ..Default::default()
        };

        let owners = collect_already_declared_owner_types(&config);
        assert_eq!(owners.len(), 1);
        assert!(owners.contains("StreamClient"));
    }
}
