import Foundation /// Body für `POST /api/v1/marketplace/authors/me` — Upsert des /// Author-Profils. Pflicht-Schritt vor dem ersten Deck-Init im /// Marketplace. struct AuthorUpsertBody: Encodable { let slug: String let displayName: String let bio: String? let avatarUrl: String? let pseudonym: Bool? enum CodingKeys: String, CodingKey { case slug case displayName case bio case avatarUrl case pseudonym } } /// Body für `POST /api/v1/marketplace/decks` — Deck-Init. /// Erstellt nur die Metadaten; Karten kommen mit der ersten `publish`. struct MarketplaceDeckInitBody: Encodable { let slug: String let title: String let description: String? let language: String? let license: String? let priceCredits: Int? let category: DeckCategory? enum CodingKeys: String, CodingKey { case slug case title case description case language case license case priceCredits case category } } /// Eine Card-Payload-Zeile für `POST /:slug/publish`. Andere Type- /// Namen als bei privaten Karten — der Server nutzt `'type-in'` statt /// `'typing'`. struct MarketplacePublishCard: Encodable { let type: String let fields: [String: String] } /// Body für `POST /api/v1/marketplace/decks/:slug/publish`. struct MarketplacePublishBody: Encodable { let semver: String let changelog: String? let cards: [MarketplacePublishCard] } /// Antwort von `POST /:slug/publish`. Enthält das aktualisierte Deck, /// die neue Version und das AI-Moderation-Verdict. struct MarketplacePublishResponse: Decodable { let deck: PublicDeck let version: PublicDeckVersion let moderation: ModerationResult struct ModerationResult: Decodable { let verdict: String let categories: [String]? let model: String? } } /// Liste von Wordeck-Marketplace-Lizenzen. Server akzeptiert beliebige /// Strings ≤ 60 Zeichen — wir bieten die kanonischen vier. enum MarketplaceLicense: String, CaseIterable { case personalUse = "Wordeck-Personal-Use-1.0" case shareAlike = "Wordeck-Share-Alike-1.0" case attribution = "Wordeck-Attribution-1.0" case proOnly = "Wordeck-Pro-Only-1.0" var label: String { switch self { case .personalUse: "Persönlicher Gebrauch" case .shareAlike: "Share-Alike (CC-BY-SA-Stil)" case .attribution: "Namensnennung (CC-BY-Stil)" case .proOnly: "Nur für Wordeck-Pro (Bezahl-Decks)" } } } /// Konvertiert eine private `Card` in eine `MarketplacePublishCard` /// mit dem korrekten Marketplace-Type und Feld-Mapping. enum MarketplaceCardConverter { static func convert(_ card: Card) -> MarketplacePublishCard? { switch card.type { case .basic, .basicReverse, .cloze, .multipleChoice: return MarketplacePublishCard(type: card.type.rawValue, fields: card.fields) case .typing: let front = card.fields["front"] ?? "" let answer = card.fields["answer"] ?? "" return MarketplacePublishCard( type: "type-in", fields: ["question": front, "expected": answer] ) } } }