//
//  GenerableModels.stencil
//  Sourcery template for generating FoundationModels-aware wrappers
//
//  Usage: Annotate your plain struct with `// sourcery: generable`
//

import Foundation
import InferenceKit

#if canImport(FoundationModels)
import FoundationModels

{% for type in types.structs where type.annotations.generable %}
// MARK: - Generated Generable Model for {{ type.name }}

/// Generable model for `{{ type.name }}`
/// This type can be used with FoundationModels but converts back to the plain `{{ type.name }}`
@available(macOS 26.0, iOS 26.0, *)
@Generable(description: "{{ type.annotations.generableDescription|default:"Generated structured output for type.name" }}")
public struct Generable{{ type.name }}: ConvertibleStructuredOutput {
    public typealias DomainModel = {{ type.name }}
    public typealias PartialDomainModel = Partial{{ type.name }}
    public typealias Partial = PartiallyGenerated
    
    {% for variable in type.storedVariables %}
        {% if not variable.annotations.skipGenerable %}

    @Guide(description: "{{ variable.annotations.guideDescription|default:variable.name }}"{% if variable.annotations.guideConstraint %}, {{ variable.annotations.guideConstraint }}{% endif %})
    var {{ variable.name }}: {% for t in types.all where t.annotations.generable and t.name == variable.typeName.array.elementTypeName.name %}[Generable{{ t.name }}]{% empty %}{% for t in types.all where t.annotations.generable and t.name == variable.typeName.name %}Generable{{ t.name }}{% empty %}{{ variable.typeName }}{% endfor %}{% endfor %}
        {% endif %}
    {% endfor %}
     
    public func toDomainModel() -> {{ type.name }} {
        return {{ type.name }}(
            {% for variable in type.storedVariables %}
                {% if not variable.annotations.skipGenerable %}
                    {{ variable.name }}: {% for t in types.all where t.annotations.generable and t.name == variable.typeName.array.elementTypeName.name %}{{ variable.name }}.map { $0.toDomainModel() }{% empty %}{% for t in types.all where t.annotations.generable and t.name == variable.typeName.name %}{{ variable.name }}.toDomainModel(){% empty %}{{ variable.name }}{% endfor %}{% endfor %}{% if not forloop.last %},{% endif %}
                {% endif %}
            {% endfor %}
        )
    }
}

// MARK: - Partial Conversion

@available(macOS 26.0, iOS 26.0, *)
extension Generable{{ type.name }}.Partial: ConvertiblePartial {
    public typealias PartialDomainModel = Partial{{ type.name }}
    
    public func toPartialDomainModel() -> Partial{{ type.name }} {
        Partial{{ type.name }}(
            {% for variable in type.storedVariables %}
                {% if not variable.annotations.skipGenerable %}
                    {{ variable.name }}: {% for t in types.all where t.annotations.generable and t.name == variable.typeName.array.elementTypeName.name %}{{ variable.name }}?.map { $0.toPartialDomainModel() }{% empty %}{% for t in types.all where t.annotations.generable and t.name == variable.typeName.name %}{{ variable.name }}?.toPartialDomainModel(){% empty %}{{ variable.name }}{% endfor %}{% endfor %},
                {% endif %}
            {% endfor %}
            generationID: String(reflecting: self.id)
        )
    }
}

{% endfor %}

// MARK: - Domain Model Extensions
{% for type in types.structs where type.annotations.generable %}
extension {{ type.name }} {
    @available(macOS 26.0, iOS 26.0, *)
    public func toGenerable() -> Generable{{ type.name }} {
        return Generable{{ type.name }}(
            {% for variable in type.storedVariables %}
                {% if not variable.annotations.skipGenerable %}
                    {{ variable.name }}: {% for t in types.all where t.annotations.generable and t.name == variable.typeName.array.elementTypeName.name %}{{ variable.name }}.map { $0.toGenerable() }{% empty %}{% for t in types.all where t.annotations.generable and t.name == variable.typeName.name %}{{ variable.name }}.toGenerable(){% empty %}{{ variable.name }}{% endfor %}{% endfor %}{% if not forloop.last %},{% endif %}
                {% endif %}
            {% endfor %}
        )
    }
}

{% endfor %}

// MARK: - Tool Generable Models
// Simple @Generable structs for use with FoundationModels Tool protocol

{% for type in types.structs where type.annotations.toolGenerable %}
/// Tool arguments for `{{ type.name }}`
@available(macOS 26.0, iOS 26.0, *)
@Generable{% if type.annotations.toolGenerableDescription %}(description: "{{ type.annotations.toolGenerableDescription }}"){% endif %}
public struct ToolGenerable{{ type.name }}: Sendable {
    {% for variable in type.storedVariables %}
        {% if not variable.annotations.skipGenerable %}
            @Guide{% if variable.annotations.guideDescription %}(description: "{{ variable.annotations.guideDescription }}"){% endif %}
            public var {{ variable.name }}: {{ variable.typeName }}
        {% endif %}
    {% endfor %}
}

{% endfor %}
#endif
