import Foundation
import SwiftASN1

/// A tiny DER assembly helper over `DER.Serializer`, used to hand-build the PKCS#12 (RFC 7292)
/// structures — bags, attributes and the v1 PBE `AlgorithmIdentifier` — that swift-certificates does
/// not provide. Each function returns a complete
/// TLV byte array so callers compose structures by nesting raw children — the one shape
/// `DER.Serializer` + `serializeRawBytes` makes clean.
enum DERBuilder {
    /// A SEQUENCE whose content is the concatenation of the given already-encoded TLV children.
    static func sequence(_ children: [[UInt8]]) -> [UInt8] {
        constructed(.sequence, children)
    }

    /// A SET whose content is the concatenation of the given children (caller orders them; DER SET OF
    /// ordering is the caller's responsibility where it matters — it does not for our attribute sets).
    static func set(_ children: [[UInt8]]) -> [UInt8] {
        constructed(.set, children)
    }

    /// A context-specific EXPLICIT `[tag]` wrapper around one already-encoded child.
    static func explicit(_ tag: UInt, _ child: [UInt8]) -> [UInt8] {
        constructed(ASN1Identifier(tagWithNumber: tag, tagClass: .contextSpecific), [child])
    }

    static func octetString(_ bytes: [UInt8]) -> [UInt8] {
        primitive(.octetString) { $0.append(contentsOf: bytes) }
    }

    static func integer(_ value: Int) -> [UInt8] {
        serialize(value)
    }

    static func oid(_ dotted: String) -> [UInt8] {
        // Our OIDs are all compile-time constants; a parse failure is a programmer error.
        let o = try! ASN1ObjectIdentifier(dotRepresentation: dotted)
        return serialize(o)
    }

    static func null() -> [UInt8] {
        serialize(ASN1Null())
    }

    // MARK: - Primitives

    static func constructed(_ id: ASN1Identifier, _ children: [[UInt8]]) -> [UInt8] {
        var s = DER.Serializer()
        // appendConstructedNode never throws for raw-byte writes.
        s.appendConstructedNode(identifier: id) { inner in
            for c in children { inner.serializeRawBytes(c) }
        }
        return s.serializedBytes
    }

    static func primitive(_ id: ASN1Identifier, _ write: (inout [UInt8]) -> Void) -> [UInt8] {
        var s = DER.Serializer()
        s.appendPrimitiveNode(identifier: id) { write(&$0) }
        return s.serializedBytes
    }

    /// Serialize any swift-asn1 `DERSerializable` value to a complete TLV.
    static func serialize<T: DERSerializable>(_ value: T) -> [UInt8] {
        var s = DER.Serializer()
        try! s.serialize(value)
        return s.serializedBytes
    }
}
