use crate::codegen::shared::binding_fields;
use crate::core::backend::{Backend, BuildConfig, BuildDependency, Capabilities, GeneratedFile, PostBuildStep};
use crate::core::config::{
    AdapterConfig, AdapterPattern, Language, ResolvedCrateConfig, TraitBridgeConfig, resolve_output_dir,
};
use crate::core::ir::{ApiSurface, TypeDef};
use std::collections::BTreeSet;
use std::path::PathBuf;

use crate::backends::swift::gen_rust_crate;
use crate::backends::swift::type_map::SwiftMapper;

mod boxes;
pub(crate) mod bridge_artifacts;
mod client;
mod dto;
mod enums;
mod errors;
mod forwarders;
pub(crate) mod opaque_handles;
mod overloads;
pub mod plugin_marshal;
pub mod service_api;
mod streaming;
pub mod trait_bridge;

pub struct SwiftBackend;

fn effective_exclude_types(config: &ResolvedCrateConfig, api: &ApiSurface) -> std::collections::HashSet<String> {
    let mut exclude_types: std::collections::HashSet<String> = config
        .ffi
        .as_ref()
        .map(|c| c.exclude_types.iter().cloned().collect())
        .unwrap_or_default();
    if let Some(swift) = &config.swift {
        exclude_types.extend(swift.exclude_types.iter().cloned());
    }
    exclude_types.extend(api.types.iter().filter(|t| t.binding_excluded).map(|t| t.name.clone()));
    exclude_types.extend(api.enums.iter().filter(|e| e.binding_excluded).map(|e| e.name.clone()));
    exclude_types.extend(api.excluded_type_paths.keys().cloned());
    // Opaque types are intentionally skipped by DTO/typealias/enum emitters, but
    // they may still appear in free-function signatures. Keep them visible to the
    // forwarder pass so functions returning opaque handles remain callable.
    exclude_types
}

fn emit_sendable_conformance(out: &mut String, type_name: &str, mark: Option<&str>, comments: &[&str]) {
    out.push_str(&crate::backends::swift::template_env::render(
        "swift_sendable_conformance.swift.jinja",
        minijinja::context! {
            type_name => type_name,
            mark => mark,
            comments => comments,
        },
    ));
}

impl Backend for SwiftBackend {
    fn name(&self) -> &str {
        "swift"
    }

    fn language(&self) -> Language {
        Language::Swift
    }

    fn capabilities(&self) -> Capabilities {
        Capabilities {
            supports_async: true,
            supports_classes: true,
            supports_enums: true,
            supports_option: true,
            supports_result: true,
            supports_callbacks: false,
            supports_streaming: true,
            supports_service_api: true,
        }
    }

    fn generate_bindings(&self, api: &ApiSurface, config: &ResolvedCrateConfig) -> anyhow::Result<Vec<GeneratedFile>> {
        let module_name = config.swift_module();
        let mapper = SwiftMapper;

        // The Swift host facade is a single compiled surface with no Rust-cfg gating, so
        // same-named cfg-variant functions (real impl + no-ORT stub fallback) must collapse to a
        // single forwarder to avoid duplicate-declaration errors. The Rust-side `gen_rust_crate`
        // bridge keeps the original multi-entry surface because it cfg-filters per configured
        // feature set itself; we hand it `original_api` below. See codegen::fn_dedup.
        let original_api = api;

        // Use the same effective feature set as the Rust-side bridge crate:
        // configured Swift dependency features plus cfg passthrough features enabled
        // by default in the generated Swift Cargo.toml.
        let core_crate_dir = config.core_crate_for_language(crate::core::config::extras::Language::Swift);
        let effective_features =
            gen_rust_crate::feature_gate::effective_swift_codegen_features(original_api, config, &core_crate_dir);
        let configured_features: std::collections::HashSet<&str> =
            effective_features.iter().map(String::as_str).collect();

        // Drop any type/enum/function whose `#[cfg(feature = "...")]` gate is not satisfied
        // by the Swift feature set BEFORE deduping. The Rust bridge crate filters its
        // `visible_*` sets with the same cfg check, so the high-level facade must do the
        // same — otherwise it emits `RustBridge.{Type}` / `RustBridge.{fn}` references for
        // items the bridge layer never compiled (e.g. `presets`/`heuristics`/`structured`
        // types absent from the Swift cargo features), which fail to resolve at compile time.
        let cfg_filtered_api = original_api.with_cfg_filtered(&configured_features);
        let deduped_api = cfg_filtered_api.with_deduped_functions();
        let api = &deduped_api;

        // Function-wrapper emission is disabled in this phase (see comment below);
        // `swift.exclude_functions` therefore has no effect on the host wrapper but
        // is still consumed by the Rust-side bridge crate via gen_rust_crate::emit.
        let exclude_types = effective_exclude_types(config, api);
        // The Rust-side gen_rust_crate uses the same `exclude_fields` set to gate which
        // fields appear in the `#[swift_bridge(init)] fn new(...)` constructor extern.
        // We mirror that set here so `intoRust()` knows when a bulk constructor extern
        // is available and which fields it accepts.
        let exclude_fields: std::collections::HashSet<String> = config
            .swift
            .as_ref()
            .map(|c| c.exclude_fields.iter().cloned().collect())
            .unwrap_or_default();

        let mut imports: BTreeSet<String> = BTreeSet::new();
        // Foundation is always included — Codable, Data, URL all live there.
        imports.insert("import Foundation".to_string());
        // RustBridge is the Swift module generated by swift-bridge from the Rust bridge crate.
        // It provides opaque type definitions and FFI entry points.
        if !api.types.is_empty() || !api.enums.is_empty() || !api.errors.is_empty() {
            imports.insert("import RustBridge".to_string());
        }

        // Extract capsule module imports from config. For each capsule type's host_type
        // (e.g., "SwiftTreeSitter.Language"), extract the module name (e.g., "SwiftTreeSitter")
        // and add an import statement.
        if let Some(swift_config) = &config.swift {
            for capsule_cfg in swift_config.capsule_types.values() {
                // Extract the first dotted component from host_type.
                // e.g., "SwiftTreeSitter.Language" → "SwiftTreeSitter"
                if let Some(module_name) = capsule_cfg.host_type.split('.').next() {
                    // Skip if module_name contains sigils (shouldn't happen, but be defensive)
                    if !module_name.is_empty() && !module_name.contains(['*', '?', '&']) {
                        imports.insert(format!("import {module_name}"));
                    }
                }
            }
        }

        let mut body = String::new();

        // When a capsule host module (e.g. `SwiftTreeSitter`) is imported, its public
        // types (`Parser`, `Tree`, `Node`, `Language`, …) collide with this package's
        // same-named opaque handles from `RustBridge`, making bare references ambiguous
        // (`'Parser' is ambiguous for type lookup`). A module-level typealias shadows the
        // imported name with the local `RustBridge` handle, disambiguating every bare
        // reference at once without qualifying each site. Capsule host types are always
        // referenced fully-qualified (`SwiftTreeSitter.Language`), so they are unaffected.
        let capsule_present = config
            .swift
            .as_ref()
            .map(|c| !c.capsule_types.is_empty())
            .unwrap_or(false);
        if capsule_present {
            // Method-bearing opaque handles (e.g. `Parser`, `Tree`, `Node`) are emitted as
            // `RustBridge` classes, not host-module typealiases, so bare references collide
            // with the capsule module's same-named types. No-method opaque handles (e.g.
            // `Language`) already get a `public typealias X = RustBridge.X` elsewhere, so they
            // are excluded here to avoid a redeclaration. The alias is `public` so it can
            // appear in public forwarder signatures.
            let mut aliases: Vec<String> = api
                .types
                .iter()
                .filter(|t| t.is_opaque && !t.methods.is_empty() && !exclude_types.contains(&t.name))
                .map(|t| format!("public typealias {0} = RustBridge.{0}", t.name))
                .collect();
            aliases.sort();
            aliases.dedup();
            if !aliases.is_empty() {
                body.push_str(&aliases.join("\n"));
                body.push_str("\n\n");
            }
        }

        // Unit serde enums (all-unit + has_serde) are emitted as `public enum S: String, Codable`
        // with serde-derived raw values. They are Codable so structs containing them as fields
        // can also derive Codable. Collect their names before the fixed-point loop so the loop
        // can accept `Named(enum_S)` fields.
        let unit_serde_enum_names: std::collections::HashSet<String> = api
            .enums
            .iter()
            .filter(|e| !exclude_types.contains(&e.name))
            .filter(|e| e.has_serde && e.variants.iter().all(|v| v.fields.is_empty()))
            .map(|e| e.name.clone())
            .collect();

        // Compute which struct DTO types will actually be emitted as first-class Swift
        // structs via a fixed-point iteration.
        //
        // A type is first-class iff:
        //   1. It is non-opaque, has_serde, non-trait, non-excluded, and has visible fields.
        //   2. Every visible field's TypeRef is "supported" given the current first-class set.
        //
        // Unit serde enums (above) are Codable and seed the initial `known_dto_names` set.
        // Struct DTOs are added iteratively: a struct becomes first-class when all its fields
        // are known-supported (primitive, String, known first-class struct, or unit serde enum).
        //
        // Convergence is guaranteed: each round only adds types (monotonically growing set).
        let candidate_types: Vec<&crate::core::ir::TypeDef> = api
            .types
            .iter()
            .filter(|t| !t.is_trait && !t.is_opaque && t.has_serde && !exclude_types.contains(&t.name))
            .filter(|t| !t.fields.is_empty())
            .collect();

        // Collect every serde data-variant enum (both untagged AND tagged) that bridges
        // as a JSON `RustString` at the FFI boundary rather than as an opaque
        // `RustBridge.{Name}Ref`. swift-bridge does not emit an `init(_ rb:Ref)` for
        // either form — untagged enums (`#[serde(untagged)]`) are emitted directly as
        // JSON, and tagged enums (default external tagging or `#[serde(tag = "…")]`)
        // are also returned as JSON strings from the bridge accessor. Containing structs
        // must decode these via JSONDecoder rather than via the broken `try {Name}(rb.field())`
        // path. The set name is kept as `untagged_enum_names` for legacy continuity; it
        // captures any Codable enum with data variants whose bridge accessor returns
        // RustString and whose Swift target type only has `init(from: Decoder)`.
        let untagged_enum_names: std::collections::HashSet<String> = api
            .enums
            .iter()
            .filter(|e| !exclude_types.contains(&e.name))
            .filter(|e| e.has_serde && e.variants.iter().any(|v| !v.fields.is_empty()))
            .map(|e| e.name.clone())
            .collect();

        // Seed with unit serde enum names + every data-variant Codable enum name (tagged +
        // untagged, all collected above). All are Codable and can appear as struct fields.
        // They are included so that `first_class_field_supported` accepts them as valid
        // field types, allowing containing structs to be emitted as first-class Swift
        // structs. The init code then routes Named fields of these types through
        // JSONDecoder rather than the Ref path.
        let mut known_dto_names: std::collections::HashSet<String> = unit_serde_enum_names.clone();
        known_dto_names.extend(untagged_enum_names.iter().cloned());
        loop {
            let prev_len = known_dto_names.len();
            for ty in &candidate_types {
                if known_dto_names.contains(&ty.name) {
                    continue;
                }
                // Check if all visible (binding-non-excluded) fields are supported given the
                // current known set (struct DTOs + unit serde enums + tagged enums).
                let all_supported = binding_fields(&ty.fields)
                    .all(|field| dto::first_class_field_supported(&field.ty, &known_dto_names));
                if all_supported {
                    known_dto_names.insert(ty.name.clone());
                }
            }
            if known_dto_names.len() == prev_len {
                break; // stable — no new types added this round
            }
        }

        // Emit typealiases for all struct types exposed by swift-bridge.
        // swift-bridge exposes types that are declared in the extern "Rust" block,
        // so we generate typealiases for all non-excluded types to provide a
        // stable Swift API that references RustBridge types.
        // Skip opaque handle types with methods — those get a full class wrapper below
        // instead of a typealias.
        let mut first_class_struct_names: Vec<String> = Vec::new();
        for ty in api
            .types
            .iter()
            .filter(|t| !t.is_trait && !exclude_types.contains(&t.name))
            .filter(|t| t.methods.is_empty() || !t.is_opaque && t.has_serde)
        {
            client::emit_doc_comment(&ty.doc, "", &mut body);
            if dto::can_emit_first_class_struct(ty, &mapper, &exclude_fields, &known_dto_names) {
                // Compute the Swift error enum name as emitted by `errors::emit_error`:
                // it uses `error.name` directly, except bare `Error` is renamed to
                // `{module_name}Error` to avoid the parser ambiguity of
                // `public enum Error: Error`. DTO call sites must reference this
                // emitted name, NOT `config.error_type_name()` (which defaults to
                // "Error" when not configured even for crates whose actual Rust
                // error type is named differently, e.g. `CrawlError`).
                let dto_error_name = api
                    .errors
                    .first()
                    .map(|e| {
                        if e.name == "Error" {
                            format!("{module_name}Error")
                        } else {
                            e.name.clone()
                        }
                    })
                    .unwrap_or_else(|| {
                        let raw = config.error_type_name();
                        if raw == "Error" {
                            format!("{module_name}Error")
                        } else {
                            raw
                        }
                    });
                first_class_struct_names.push(ty.name.clone());
                dto::emit_first_class_struct(
                    ty,
                    &mapper,
                    &exclude_fields,
                    &known_dto_names,
                    &unit_serde_enum_names,
                    &untagged_enum_names,
                    &dto_error_name,
                    &configured_features,
                    &mut body,
                );
            } else {
                body.push_str(&crate::backends::swift::template_env::render(
                    "typealias.jinja",
                    minijinja::context! {
                        name => &ty.name,
                    },
                ));
            }
            body.push('\n');
        }

        // Emit typealiases for *Ref and *RefMut variants of first-class structs.
        // These are referenced in the `init(_ rb: RustBridge.{Type}Ref)` initializers
        // and the `func intoRust() -> RustBridge.{Type}` methods, which swift-bridge
        // synthesizes as part of the class hierarchy. Without these typealiases, the
        // generated first-class struct code references undefined types in RustBridge.
        for type_name in &first_class_struct_names {
            body.push_str(&crate::backends::swift::template_env::render(
                "typealias.jinja",
                minijinja::context! {
                    name => &format!("{type_name}Ref"),
                },
            ));
            body.push('\n');
            body.push_str(&crate::backends::swift::template_env::render(
                "typealias.jinja",
                minijinja::context! {
                    name => &format!("{type_name}RefMut"),
                },
            ));
            body.push('\n');
        }

        // Collect result enum names from trait bridges — these are emitted as first-class
        // Swift enums that JSON-decode locally, so they don't call a Rust-side from_json fn.
        let result_type_enums: std::collections::HashSet<String> = config
            .trait_bridges
            .iter()
            .filter_map(|b| b.result_type.as_deref().map(|s| s.to_string()))
            .collect();

        // Enums are emitted as native Swift enums with unit variants only.
        // They are NOT typealiased to RustBridge since swift-bridge's automatic generation
        // is unreliable (not all enum types are exposed). Instead, we emit them directly
        // as Swift enums that mirror the unit variants from the Rust bridge enum wrapper.
        for en in api.enums.iter().filter(|e| !exclude_types.contains(&e.name)) {
            if result_type_enums.contains(&en.name) {
                // Result-type enums are emitted without intoRust() because Swift decodes them
                // locally via JSONDecoder, not via a Rust-side from_json function.
                enums::emit_enum_without_into_rust(en, &mut body, &mapper, &known_dto_names);
            } else {
                enums::emit_enum(
                    en,
                    &mut body,
                    &mapper,
                    &known_dto_names,
                    &config.untagged_union_text_types,
                );
            }
            body.push('\n');
        }

        for error in &api.errors {
            errors::emit_error(error, &module_name, &mut body, &mapper);
            body.push('\n');
        }

        // Emit Swift class wrappers only for opaque handle types that expose methods
        // AND have an explicit `client_constructor_body` in alef.toml.  Without an
        // override the Rust bridge crate does not emit a `create_<type>` shim
        // (see gen_rust_crate::mod.rs), so a class wrapper with `init(apiKey,
        // baseUrl)` would reference a non-existent bridge function.
        // Opaque types without an override are returned by Rust APIs, not
        // constructed in Swift — they remain accessible via their `RustBridge`
        // typealiases / direct method shims.
        let client_constructor_types: std::collections::HashSet<&str> = config
            .swift
            .as_ref()
            .map(|c| c.client_constructor_body.keys().map(String::as_str).collect())
            .unwrap_or_default();
        // Collect first-class struct type names for streaming chunk decode dispatch.
        // Must match the actual emission decision in `can_emit_first_class_struct`
        // (Codable struct with all-supported fields).
        // Types failing that check are emitted as typealiases to RustBridge.X and so
        // do not have a `func intoRust()` extension or auto-derived Codable conformance.
        let first_class_types: std::collections::HashSet<String> = api
            .types
            .iter()
            .filter(|t| !t.is_trait && !exclude_types.contains(&t.name))
            .filter(|t| dto::can_emit_first_class_struct(t, &mapper, &exclude_fields, &known_dto_names))
            .map(|t| t.name.clone())
            .collect();
        // Tracks every type that has already received an `extension RustBridge.X:
        // @unchecked Sendable {}` so the streaming-specific emissions and the
        // catch-all block below never declare the conformance twice (Swift warns
        // on a redundant conformance).
        let mut sendable_emitted: std::collections::HashSet<String> = std::collections::HashSet::new();
        for ty in api.types.iter().filter(|t| {
            !t.is_trait
                && !exclude_types.contains(&t.name)
                && !t.methods.is_empty()
                && (t.is_opaque || !t.has_serde)
                && client_constructor_types.contains(t.name.as_str())
        }) {
            client::emit_client_class(
                ty.name.as_str(),
                &ty.methods,
                &mapper,
                config,
                &first_class_types,
                &mut body,
            );
            body.push('\n');
            // Emit `@unchecked Sendable` conformance for every StreamHandle owned by this
            // client type.  swift-bridge emits opaque types as plain Swift classes without
            // `Sendable`; Swift 6 strict-concurrency rejects passing them across
            // `Task.detached` boundaries.  The Rust side wraps a Mutex<stream> and a
            // tokio Runtime — both thread-safe — so `@unchecked` is correct.
            // Collect streaming adapters owned by this client type.
            let streaming_adapters: Vec<&AdapterConfig> = config
                .adapters
                .iter()
                .filter(|a| matches!(a.pattern, AdapterPattern::Streaming))
                .filter(|a| !a.skip_languages.iter().any(|l| l == "swift"))
                .filter(|a| a.owner_type.as_deref() == Some(ty.name.as_str()))
                .collect();
            if !streaming_adapters.is_empty() {
                // The streaming methods capture `self.inner` (typed `RustBridge.{ty.name}`)
                // inside a `Task.detached` closure.  Swift 6 strict-concurrency requires every
                // captured value to be `Sendable`.  The Rust type is `Send + Sync`, so
                // `@unchecked` is correct.
                let inner_ty = ty.name.as_str();
                if sendable_emitted.insert(inner_ty.to_string()) {
                    emit_sendable_conformance(
                        &mut body,
                        inner_ty,
                        Some("streaming client inner"),
                        &[
                            "swift-bridge opaque types are not automatically Sendable.",
                            "Captured by Task.detached in streaming methods — Rust type is Send + Sync.",
                        ],
                    );
                }
            }
            for adapter in &streaming_adapters {
                client::emit_stream_handle_sendable(adapter, ty.name.as_str(), &mut body);
            }
            // Emit `@unchecked Sendable` conformance for every opaque param type
            // used in streaming adapter calls.  These types are passed into
            // `Task.detached` closures; Swift 6 strict-concurrency requires them
            // to be `Sendable`.  They are swift-bridge opaque classes backed by
            // Rust types that are `Send + Sync`, so `@unchecked` is correct.
            {
                for adapter in &streaming_adapters {
                    for param in &adapter.params {
                        let simple_ty = param.ty.rsplit("::").next().unwrap_or(&param.ty).to_string();
                        if sendable_emitted.insert(simple_ty.clone()) {
                            emit_sendable_conformance(
                                &mut body,
                                &simple_ty,
                                Some("streaming request param"),
                                &[
                                    "swift-bridge opaque types are not automatically Sendable.",
                                    "Passed into Task.detached for streaming — Rust type is Send + Sync.",
                                ],
                            );
                        }
                    }
                }
            }
        }

        // Emit lightweight convenience overloads for functions whose first parameter
        // is TypeRef::Bytes or TypeRef::Path. Discovery is IR-driven: the emitter
        // walks api.functions and detects candidates by signature shape, never by name.
        client::emit_convenience_wrappers(api, &exclude_types, &mut body);

        // Emit JSON-string convenience overloads for functions that take config-like
        // struct parameters. These allow e2e tests to pass JSON strings for config
        // objects, automatically decoding them via the `*FromJson` helpers.
        // Also emit the _loadBytesFromPathOrUtf8 helper for path-or-content resolution.
        overloads::emit_json_string_overloads(api, &exclude_types, &mut body);

        // Emit public Swift forwarding functions for `*FromJson` helpers.
        // The Rust bridge crate exposes `{type_snake}_from_json` as a swift-bridge
        // free function accessible via `RustBridge.{swiftName}(json)`. Without a
        // forwarding wrapper in this module, callers would need the `RustBridge.`
        // prefix, which the generated e2e test layer doesn't use.
        overloads::emit_from_json_forwarders(
            api,
            &exclude_types,
            &mapper,
            &exclude_fields,
            &known_dto_names,
            &mut body,
        );

        // Emit Swift protocol + adapter class for OptionsField inbound trait bridges.
        // Each bridge in `config.trait_bridges` where `bind_via = "options_field"` gets:
        //   - A `public protocol {Trait}Protocol: AnyObject` with one method per trait method.
        //   - A private adapter class that wraps the protocol conformer.
        //   - A factory func `make{TraitCamel}Handle(...)`.
        // NOTE: The `Swift{Trait}Box` class is emitted into Sources/RustBridge/ (separate file)
        // because it is referenced by the swift-bridge generated @_cdecl shims there.
        bridge_artifacts::emit_inbound_protocols(api, config, &exclude_types, &mut body);

        // Emit top-level public forwarders for every free function in `api.functions`
        // that is not already covered by the bytes/path convenience wrappers or
        // the `*FromJson` forwarders. Without these,
        // `embedTexts`, `listEmbeddingPresets`, `getEmbeddingPreset`, `renderPdfPageToPng`,
        // `getExtensionsForMime`, `detectMimeType`, and the various `list_*` registry
        // helpers are only reachable as `RustBridge.fnName(...)` — which requires
        // users of the host module to also `import RustBridge`, violating
        // the alef binding-parity promise that every public Rust free function is
        // re-exposed as a top-level public function on the host module.
        let client_class_names: std::collections::HashSet<String> =
            client_constructor_types.iter().map(|&s| s.to_string()).collect();
        let all_enum_names: std::collections::HashSet<String> = unit_serde_enum_names
            .iter()
            .chain(untagged_enum_names.iter())
            .cloned()
            .collect();
        forwarders::emit_free_function_forwarders(
            api,
            config,
            &known_dto_names,
            &all_enum_names,
            &client_class_names,
            &exclude_types,
            &mut body,
        );

        // Emit top-level public forwarders for trait-bridge register/unregister/clear
        // entry points. These are synthesised from `[[trait_bridges]]` in alef.toml
        // (not present in `api.functions`), so the free-function forwarder pass above
        // does not reach them. Mirrors the extendr/napi/php fixes — every native
        // binding exposes `register_<trait>` / `unregister_<trait>` / `clear_<trait>`
        // as part of the public plugin registration surface.
        forwarders::emit_trait_bridge_forwarders(config, &mut body);

        // Emit AsyncThrowingStream free functions for streaming adapters whose owner
        // type has no Swift client class wrapper (no client_constructor_body in alef.toml).
        // For these adapters, emit_client_class is never called so the streaming methods
        // must be top-level free functions taking the handle as the first parameter.
        client::emit_streaming_free_functions(config, &first_class_types, &mut sendable_emitted, &mut body);

        // Emit `@unchecked Sendable` conformance for all remaining opaque types.
        // swift-bridge emits opaque types as plain Swift classes without `Sendable`
        // conformance. Swift 6 strict-concurrency rejects passing non-Sendable values
        // across `Task.detached` boundaries, which occurs in async function forwarders
        // that wrap blocking Rust calls. All swift-bridge opaque types wrap Rust
        // pointers to Send + Sync types, so `@unchecked` is correct for all of them.
        //
        // The set covers both genuinely-opaque IR types and types returned across the
        // bridge as opaque handles (`compute_handle_returned_types`). The latter is
        // essential: a result struct such as `ScrapeResult` may also be emitted as a
        // first-class Swift struct, but it is still bridged as an opaque
        // `RustBridge.ScrapeResult` class that an async forwarder returns out of a
        // `Task.detached`. `sendable_emitted` prevents re-declaring a conformance the
        // streaming emissions above already produced.
        {
            // Emit `@unchecked Sendable` conformance for every non-trait type that has a
            // `RustBridge.<T>` opaque-class shadow (i.e. anything not excluded from the
            // binding). swift-bridge emits all of these as plain classes wrapping a Rust
            // pointer to a Send + Sync type, so the conformance is safe. Previously the
            // filter only included `is_opaque || handle_returned` — that missed
            // parameter-only DTOs such as `BatchFileItem` / `BatchBytesItem`, which then
            // failed Swift 6 strict-concurrency checks when captured by async forwarders'
            // `Task.detached` closures.
            for ty in api
                .types
                .iter()
                .filter(|t| !t.is_trait && !exclude_types.contains(&t.name))
            {
                if sendable_emitted.insert(ty.name.clone()) {
                    emit_sendable_conformance(
                        &mut body,
                        &ty.name,
                        None,
                        &["swift-bridge opaque type used across Task.detached boundaries — Rust type is Send + Sync."],
                    );
                }
            }

            // Second pass: any Named type referenced as `Vec<Named>` (or bare Named) in
            // an async forwarder return position also needs `@unchecked Sendable`.
            // Such types may have been filtered out of `api.types` above (e.g. when an
            // alef(skip)-annotated cfg-gated stub shadows the canonical) but still
            // surface in the swift-bridge extern as `type T;` because they appear in a
            // public return signature, and the forwarder body crosses the
            // `Task.detached.value` boundary with `[T]`. Without this pass, Swift 6
            // strict-concurrency rejects the closure return.
            //
            // Collects from `api.functions` only (async, free fns); methods are handled
            // by their owner's client-class Sendable emission path.
            fn collect_async_vec_named<'a>(
                ty: &'a crate::core::ir::TypeRef,
                names: &mut std::collections::HashSet<&'a str>,
            ) {
                use crate::core::ir::TypeRef;
                match ty {
                    TypeRef::Vec(inner) | TypeRef::Optional(inner) => {
                        collect_async_vec_named(inner, names);
                    }
                    TypeRef::Named(n) => {
                        names.insert(n.as_str());
                    }
                    _ => {}
                }
            }

            let mut referenced_async_named: std::collections::HashSet<&str> = std::collections::HashSet::new();
            for f in &api.functions {
                if !f.is_async || f.binding_excluded {
                    continue;
                }
                collect_async_vec_named(&f.return_type, &mut referenced_async_named);
            }
            // Note: deliberately do NOT skip `exclude_types` here — the whole point of
            // this pass is to catch types whose canonical definition is shadowed by an
            // `alef(skip)`-annotated cfg-gated stub (which lands them in `exclude_types`
            // via `binding_excluded`). The swift-bridge extern still declares them as
            // `type T;` because they appear in a public return signature, and the
            // forwarder needs `T` to be Sendable to cross `Task.detached.value`.
            for name in referenced_async_named {
                if sendable_emitted.insert(name.to_string()) {
                    emit_sendable_conformance(
                        &mut body,
                        name,
                        None,
                        &[
                            "swift-bridge opaque type referenced in async forwarder return — Rust type is Send + Sync.",
                            "Auto-included even when the IR filter excluded it (e.g. cfg-gated alef(skip) stub).",
                        ],
                    );
                }
            }
        }

        // swift-format-ignore-file tells swift-format to skip this file entirely.
        // Generated Swift code violates several formatting rules (long lines,
        // trailing commas in function signatures, etc.) that cannot be trivially
        // fixed without a full reformatter post-processing step.
        let imports = imports.iter().cloned().collect::<Vec<_>>().join("\n");
        let mut content = crate::backends::swift::template_env::render(
            "swift_module_header.swift.jinja",
            minijinja::context! {
                imports => imports,
            },
        );
        content.push_str(&body);

        let base_dir = resolve_output_dir(config.output_paths.get("swift"), &config.name, "packages/swift");
        let base_path = PathBuf::from(&base_dir);
        // Explicit `[crates.output] swift = "..."` is treated as the final
        // package directory: the user controls layout. Without an override the
        // backend constructs the canonical SwiftPM `Sources/<Module>/` layout
        // under the template-derived base.
        let path = if config.explicit_output.swift.is_some() {
            base_path.join(format!("{module_name}.swift"))
        } else {
            base_path
                .join("Sources")
                .join(&module_name)
                .join(format!("{module_name}.swift"))
        };

        let mut files = vec![GeneratedFile {
            path,
            content,
            generated_header: false,
        }];

        // Phase 2C: emit the Rust-side swift-bridge crate
        let rust_crate_files = gen_rust_crate::emit(original_api, config)?;
        files.extend(rust_crate_files);

        // Phase 2D: if the swift-bridge build output exists in target/*/out/, emit the
        // properly prefixed bridge files into Sources/RustBridge{,C}/.
        // These files are generated by `cargo build -p {crate}-swift` via
        // swift_bridge_build and must be present in the SwiftPM package for it to compile.
        let binding_crate_name = format!("{}-swift", &api.crate_name);
        let base_dir = resolve_output_dir(config.output_paths.get("swift"), &config.name, "packages/swift");
        let package_root = PathBuf::from(&base_dir)
            .ancestors()
            // walk up until we find Sources/ sibling, or stop at root
            .find(|p| p.join("Sources").is_dir())
            .map(|p| p.to_path_buf())
            // fallback: use the configured base dir stripped of Sources/<Module>
            .unwrap_or_else(|| {
                // base_dir is e.g. "packages/swift/Sources/SampleLlm"
                // go up two levels to get "packages/swift"
                PathBuf::from(&base_dir)
                    .parent()
                    .and_then(|p| p.parent())
                    .map(|p| p.to_path_buf())
                    .unwrap_or_else(|| PathBuf::from("packages/swift"))
            });
        if let Some(bridge_files) =
            bridge_artifacts::emit_swift_bridge_files(&api.crate_name, &binding_crate_name, &package_root)?
        {
            files.extend(bridge_files);
        }

        // Emit Swift{Trait}Box.swift into Sources/RustBridge/ for every OptionsField bridge.
        // This class is referenced by the @_cdecl shims generated into that same directory
        // by swift-bridge, so it MUST live in the RustBridge module, not the host module.
        let rust_bridge_sources = package_root.join("Sources").join("RustBridge");
        for box_file in boxes::emit_inbound_box_files(api, config, &rust_bridge_sources) {
            files.push(box_file);
        }

        // Emit Swift{Trait}Box.swift and ZSwiftPluginHelpers.swift for every FunctionParam bridge.
        // For Box methods generated in RustBridge, exclude all non-primitive named types
        // since they cannot be directly imported/used in the RustBridge module.
        // These types will be marshalled as JSON strings at the trait boundary.
        let mut box_exclude = exclude_types.clone();
        for ty in &api.types {
            // Exclude all non-opaque, non-trait types with serde (struct/record types)
            // and all non-opaque types that are visible in the binding
            if !ty.is_trait && !ty.is_opaque && ty.has_serde {
                box_exclude.insert(ty.name.clone());
            }
        }
        for box_file in boxes::emit_function_param_box_files(api, config, &rust_bridge_sources, &box_exclude) {
            files.push(box_file);
        }

        // Emit class triples for opaque handle types marked with #[swift_bridge(already_declared)]
        // in the Rust extern blocks. These are primarily streaming adapter owner types that need
        // Swift class wrappers since swift-bridge skips generation for already_declared types.
        if let Some(opaque_file) = opaque_handles::emit_opaque_class_declarations(config, &rust_bridge_sources) {
            files.push(opaque_file);
        }

        // Emit trait bridge protocol and adapter files for outbound plugins.
        // Swift{Trait}Bridge.swift files are emitted into Sources/RustBridge/ so that Box classes
        // in the same target can reference the bridge protocols.
        let trait_bridge_configs: Vec<(String, &TraitBridgeConfig, &TypeDef)> = config
            .trait_bridges
            .iter()
            .filter_map(|b| {
                api.types
                    .iter()
                    .find(|t| t.is_trait && t.name == b.trait_name)
                    .map(|t| (b.trait_name.clone(), b, t))
            })
            .collect();

        let module_dir = if config.explicit_output.swift.is_some() {
            base_path.clone()
        } else {
            base_path.join("Sources").join(&module_name)
        };

        for (filename, content) in
            trait_bridge::gen_trait_bridge_files(&trait_bridge_configs, &box_exclude, &first_class_types)
        {
            // Trait bridge protocol files (Swift{Trait}Bridge.swift and SwiftPluginBridge.swift)
            // go into the RustBridge target so they are accessible from Box classes in the same target.
            let path = rust_bridge_sources.join(&filename);
            files.push(GeneratedFile {
                path,
                content,
                generated_header: false,
            });
        }

        // Emit ref-property extensions (property-access aliases for zero-param string-returning methods).
        // The extension filters via method.binding_excluded to skip methods that swift-bridge
        // silently drops (e.g. signatures with non-FFI-friendly returns like SocketAddr).
        if let Some((filename, content)) = bridge_artifacts::emit_ref_property_extensions(api) {
            let path = module_dir.join(&filename);
            files.push(GeneratedFile {
                path,
                content,
                generated_header: true,
            });
        }

        // Emit bridge registration overloads file (register/unregister convenience functions + stub adapters).
        if let Some((filename, content)) = trait_bridge::gen_bridge_registration_overloads_file(&trait_bridge_configs) {
            let path = module_dir.join(&filename);
            files.push(GeneratedFile {
                path,
                content,
                generated_header: false,
            });
        }

        Ok(files)
    }

    fn generate_service_api(
        &self,
        api: &ApiSurface,
        config: &ResolvedCrateConfig,
    ) -> anyhow::Result<Vec<GeneratedFile>> {
        service_api::generate(api, config)
    }

    fn build_config(&self) -> Option<BuildConfig> {
        Some(BuildConfig {
            tool: "swift",
            crate_suffix: "-swift",
            build_dep: BuildDependency::None,
            // Build the Rust bridge crate first so swift-bridge codegen produces
            // the Swift glue files that the Swift Package consumes.
            // Scope the build to only the Swift bridge crate to avoid rebuilding unrelated
            // binding crates whose post-build processors may rewrite their generated files.
            post_build: vec![PostBuildStep::RunCommand {
                cmd: "cargo",
                args: vec![
                    "build",
                    "--manifest-path",
                    "packages/swift/rust/Cargo.toml",
                    "--release",
                ],
            }],
        })
    }

    fn build_config_with_config(&self, config: &ResolvedCrateConfig) -> Option<BuildConfig> {
        // Start from the no-config fallback so the cargo build RunCommand stays
        // the single source of truth, then append a MaterializeSwiftBridge step
        // that re-runs the glue/header materialization AFTER that build. The
        // generate-phase materialization at `emit_swift_bridge_files` runs before
        // this build, so on a clean build it copies stale (or missing) output;
        // re-materializing here picks up the freshly-built target/*/out artifacts.
        let mut build_config = self.build_config()?;

        // Use the hyphenated crate name so it matches the cargo build output dir
        // prefix `{config.name}-swift-<hash>` (the generate phase uses
        // `api.crate_name`, which may be underscored and mismatch).
        let binding_crate_name = format!("{}-swift", config.name);

        // Compute the Swift package root the same way the generate phase does:
        // resolve the configured output dir, then walk up to the dir containing
        // `Sources/`, with the same `packages/swift` fallback chain.
        let base_dir = resolve_output_dir(config.output_paths.get("swift"), &config.name, "packages/swift");
        let package_root = PathBuf::from(&base_dir)
            .ancestors()
            .find(|p| p.join("Sources").is_dir())
            .map(|p| p.to_path_buf())
            .unwrap_or_else(|| {
                PathBuf::from(&base_dir)
                    .parent()
                    .and_then(|p| p.parent())
                    .map(|p| p.to_path_buf())
                    .unwrap_or_else(|| PathBuf::from("packages/swift"))
            });

        build_config.post_build.push(PostBuildStep::MaterializeSwiftBridge {
            binding_crate_name,
            package_root: package_root.to_string_lossy().to_string(),
        });

        Some(build_config)
    }
}
