import type { CreateCharacterParams, UpdateCharacterParams } from "@/features/characters/api/character-api"; import type { CharacterDetail } from "@/features/characters/model/types"; import type { BackgroundDraft, CharacterOptionalFieldsValue, MemoryDraft, RelationshipDraft, TraitDraft } from "@/features/characters/components/CharacterOptionalFields"; type RelationshipRequest = NonNullable[number]; type TraitRequest = NonNullable[number]; type BackgroundRequest = NonNullable[number]; type MemoryRequest = NonNullable[number]; type CreateOptionalRequest = Partial; type MutableCreateOptionalRequest = { -readonly [Key in keyof CreateOptionalRequest]: CreateOptionalRequest[Key] }; function emptyRelationship(): RelationshipDraft { return { personName: "", relationshipName: "", description: "", importance: "", relationshipType: "", currentStatus: "" }; } function emptyTrait(): TraitDraft { return { trait: "", description: "" }; } function emptyBackground(): BackgroundDraft { return { topic: "", description: "" }; } function emptyMemory(): MemoryDraft { return { title: "", content: "", emotion: "" }; } export function createEmptyCharacterOptionalFields(): CharacterOptionalFieldsValue { return { age: "", gender: "", mbti: "", speechPattern: "", speechStyle: "", appearance: "", region: "", originalTitle: "", originalLink: "", originalTitleTouched: false, originalLinkTouched: false, characterType: "", tags: "", hobbies: "", values: "", goals: "", relationships: [emptyRelationship()], personalities: [emptyTrait()], backgrounds: [emptyBackground()], memories: [emptyMemory()] }; } export function characterOptionalFieldsFromDetail(character: CharacterDetail): CharacterOptionalFieldsValue { return { age: character.age === null ? "" : String(character.age), gender: character.gender ?? "", mbti: character.mbti ?? "", speechPattern: character.speechPattern ?? "", speechStyle: character.speechStyle ?? "", appearance: character.appearance ?? "", region: character.region, originalTitle: "", originalLink: "", originalTitleTouched: false, originalLinkTouched: false, characterType: character.characterType, tags: character.tags.join("\n"), hobbies: character.hobbies.join("\n"), values: character.values.join("\n"), goals: character.goals.join("\n"), relationships: character.relationships.length === 0 ? [emptyRelationship()] : character.relationships.map((row) => ({ ...row, importance: String(row.importance) })), personalities: character.personalities.length === 0 ? [emptyTrait()] : character.personalities, backgrounds: character.backgrounds.length === 0 ? [emptyBackground()] : character.backgrounds, memories: character.memories.length === 0 ? [emptyMemory()] : character.memories, }; } function trimOptional(value: string): string | undefined { const trimmed = value.trim(); return trimmed.length === 0 ? undefined : trimmed; } function trimNullable(value: string): string | null { return trimOptional(value) ?? null; } function splitList(value: string): readonly string[] { return value.split(/[\n,]/).map((item) => item.trim()).filter((item) => item.length > 0); } function hasRelationship(row: RelationshipDraft): boolean { return [row.personName, row.relationshipName, row.description, row.importance, row.relationshipType, row.currentStatus].some((value) => value.trim().length > 0); } function relationships(rows: readonly RelationshipDraft[]): readonly RelationshipRequest[] { return rows.filter(hasRelationship).map((row) => { const importance = Number.parseInt(row.importance.trim(), 10); return { personName: row.personName.trim(), relationshipName: row.relationshipName.trim(), description: row.description.trim(), importance: Number.isNaN(importance) ? 0 : importance, relationshipType: row.relationshipType.trim(), currentStatus: row.currentStatus.trim(), }; }); } function traits(rows: readonly TraitDraft[]): readonly TraitRequest[] { return rows.filter((row) => row.trait.trim().length > 0 || row.description.trim().length > 0).map((row) => ({ trait: row.trait.trim(), description: row.description.trim() })); } function backgrounds(rows: readonly BackgroundDraft[]): readonly BackgroundRequest[] { return rows.filter((row) => row.topic.trim().length > 0 || row.description.trim().length > 0).map((row) => ({ topic: row.topic.trim(), description: row.description.trim() })); } function memories(rows: readonly MemoryDraft[]): readonly MemoryRequest[] { return rows.filter((row) => row.title.trim().length > 0 || row.content.trim().length > 0 || row.emotion.trim().length > 0).map((row) => ({ title: row.title.trim(), content: row.content.trim(), emotion: row.emotion.trim() })); } export function toCreateCharacterOptionalRequest(value: CharacterOptionalFieldsValue): CreateOptionalRequest { const request: MutableCreateOptionalRequest = {}; const age = trimOptional(value.age); const gender = trimOptional(value.gender); const mbti = trimOptional(value.mbti); const speechPattern = trimOptional(value.speechPattern); const speechStyle = trimOptional(value.speechStyle); const appearance = trimOptional(value.appearance); const region = trimOptional(value.region); const originalTitle = trimOptional(value.originalTitle); const originalLink = trimOptional(value.originalLink); const characterType = trimOptional(value.characterType); const tags = splitList(value.tags); const hobbies = splitList(value.hobbies); const values = splitList(value.values); const goals = splitList(value.goals); const relationshipRows = relationships(value.relationships); const personalityRows = traits(value.personalities); const backgroundRows = backgrounds(value.backgrounds); const memoryRows = memories(value.memories); if (age !== undefined) request.age = age; if (gender !== undefined) request.gender = gender; if (mbti !== undefined) request.mbti = mbti; if (speechPattern !== undefined) request.speechPattern = speechPattern; if (speechStyle !== undefined) request.speechStyle = speechStyle; if (appearance !== undefined) request.appearance = appearance; if (region !== undefined) request.region = region; if (originalTitle !== undefined) request.originalTitle = originalTitle; if (originalLink !== undefined) request.originalLink = originalLink; if (characterType !== undefined) request.characterType = characterType; if (tags.length > 0) request.tags = tags; if (hobbies.length > 0) request.hobbies = hobbies; if (values.length > 0) request.values = values; if (goals.length > 0) request.goals = goals; if (relationshipRows.length > 0) request.relationships = relationshipRows; if (personalityRows.length > 0) request.personalities = personalityRows; if (backgroundRows.length > 0) request.backgrounds = backgroundRows; if (memoryRows.length > 0) request.memories = memoryRows; return request; } export function toUpdateCharacterOptionalRequest(value: CharacterOptionalFieldsValue): UpdateCharacterParams["request"] { const tags = splitList(value.tags); const hobbies = splitList(value.hobbies); const values = splitList(value.values); const goals = splitList(value.goals); const relationshipRows = relationships(value.relationships); const personalityRows = traits(value.personalities); const backgroundRows = backgrounds(value.backgrounds); const memoryRows = memories(value.memories); return { age: trimNullable(value.age), gender: trimNullable(value.gender), mbti: trimNullable(value.mbti), speechPattern: trimNullable(value.speechPattern), speechStyle: trimNullable(value.speechStyle), appearance: trimNullable(value.appearance), ...(value.originalTitleTouched ? { originalTitle: trimNullable(value.originalTitle) } : {}), ...(value.originalLinkTouched ? { originalLink: trimNullable(value.originalLink) } : {}), characterType: trimNullable(value.characterType), tags: tags.length === 0 ? null : tags, hobbies: hobbies.length === 0 ? null : hobbies, values: values.length === 0 ? null : values, goals: goals.length === 0 ? null : goals, relationships: relationshipRows.length === 0 ? null : relationshipRows, personalities: personalityRows.length === 0 ? null : personalityRows, backgrounds: backgroundRows.length === 0 ? null : backgroundRows, memories: memoryRows.length === 0 ? null : memoryRows, }; }