import { createApiClient } from './create-api-client'; const API_URL = typeof window === 'undefined' ? (process.env.INTERNAL_API_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api') : (process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api'); export const apiClient = createApiClient(API_URL); // ── Type Definitions ───────────────────────────────────────────────── export interface Project { id: string; title: string; description?: string; prompt: string; status: ProjectStatus; progress: number; language: string; aspectRatio: string; videoStyle: string; cinematicReference?: string; targetDuration: number; creditsUsed: number; thumbnailUrl?: string; finalVideoUrl?: string; errorMessage?: string; scriptJson?: ScriptJson; scriptVersion: number; scenes?: Scene[]; renderJobs?: RenderJob[]; sourceType?: 'MANUAL' | 'X_TWEET' | 'YOUTUBE'; sourceTweetData?: Record; // SEO Power Engine seoTitle?: string; seoDescription?: string; seoKeywords?: string[]; seoTitleAlts?: string[]; seoScore?: number; socialContent?: { youtubeTitle?: string; youtubeDescription?: string; tiktokCaption?: string; instagramCaption?: string; twitterText?: string; }; createdAt: string; updatedAt: string; completedAt?: string; } export type ProjectStatus = | 'DRAFT' | 'GENERATING_SCRIPT' | 'PENDING' | 'GENERATING_MEDIA' | 'RENDERING' | 'COMPLETED' | 'FAILED'; export interface Scene { id: string; order: number; title?: string; narrationText: string; visualPrompt: string; subtitleText?: string; duration: number; transitionType: string; mediaAssets?: MediaAsset[]; } export interface MediaAsset { id: string; type: string; url?: string; fileName?: string; mimeType?: string; sizeBytes?: number; durationMs?: number; aiProvider?: string; } export interface RenderJob { id: string; status: string; currentStage?: string; progress?: number; attemptNumber: number; processingTimeMs?: number; errorMessage?: string; finalVideoUrl?: string; createdAt: string; startedAt?: string; completedAt?: string; logs?: RenderLog[]; } export interface RenderLog { id: string; stage: string; level: string; message: string; durationMs?: number; createdAt: string; } export interface ScriptJson { metadata: { title: string; description: string; totalDurationSeconds: number; language: string; hashtags: string[]; }; seo: { title: string; description: string; keywords: string[]; hashtags: string[]; trendingHashtags?: string[]; estimatedSearchVolume?: string; schemaMarkup: Record; }; seoTitleAlternatives?: string[]; seoScore?: number; scenes: Array<{ order: number; title?: string; narrationText: string; visualPrompt: string; subtitleText: string; durationSeconds: number; transitionType: string; }>; musicPrompt: string; voiceStyle: string; socialContent?: { youtubeTitle: string; youtubeDescription: string; tiktokCaption: string; instagramCaption: string; twitterText: string; }; } export interface PaginatedResponse { data: T[]; meta: { total: number; page: number; limit: number; totalPages: number; }; } export interface CreateProjectPayload { title: string; description?: string; prompt: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; seoKeywords?: string[]; referenceUrl?: string; } export interface UpdateProjectPayload { title?: string; description?: string; prompt?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; seoKeywords?: string[]; } export interface CreditBalance { balance: number; remaining: number; total: number; plan: string; monthlyUsed: number; monthlyLimit: number; } export interface Template { id: string; title: string; description?: string; thumbnailUrl?: string; previewVideoUrl?: string; category: string; tags: string[]; language: string; usageCount: number; rating: number; isFeatured: boolean; } export interface DashboardStats { totalProjects: number; completedVideos: number; totalCreditsUsed: number; creditsRemaining: number; activeRenderJobs: number; recentProjects: Project[]; } // Tweet Types export interface TweetAuthor { id: string; name: string; username: string; avatarUrl: string; followersCount: number; verified: boolean; } export interface TweetMetrics { replies: number; retweets: number; likes: number; views: number; engagementRate: number; } export interface TweetMedia { type: 'photo' | 'video' | 'gif'; url: string; thumbnailUrl?: string; width: number; height: number; } export interface ParsedTweet { id: string; url: string; text: string; createdAt: string; author: TweetAuthor; metrics: TweetMetrics; media: TweetMedia[]; quotedTweet?: ParsedTweet; isThread: boolean; threadTweets?: ParsedTweet[]; } export interface TweetPreview { tweet: ParsedTweet; suggestedTitle: string; suggestedPrompt: string; viralScore: number; contentType: 'tweet' | 'thread' | 'quote_tweet'; estimatedDuration: number; } export interface CreateFromTweetPayload { tweetUrl: string; title?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; } export interface CreateFromYoutubePayload { youtubeUrl: string; title?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; } export interface CreateFromDocumentPayload { file: File; title?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; } export interface CreateFromTextPayload { text: string; title?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; } export interface ExtractDocumentTopicsPayload { file: File; } export interface ExtractDocumentTopicsResponse { text: string; topics: string[]; originalFilename: string; } export interface CreateFromExtractedTextPayload { text: string; topic: string; originalFilename?: string; language?: string; aspectRatio?: string; videoStyle?: string; cinematicReference?: string; targetDuration?: number; } export interface Notification { id: string; userId: string; type: string; title: string; message?: string | null; metadata?: Record | null; isRead: boolean; createdAt: string; updatedAt: string; } export interface NotificationListResponse { data: Notification[]; meta: { total: number; page: number; limit: number; totalPages: number; unreadCount: number; }; } // ── API Functions ──────────────────────────────────────────────────── export const authApi = { login: (data: any) => apiClient.post('/auth/login', data).then((r) => r.data), register: (data: any) => apiClient.post('/auth/register', data).then((r) => r.data), }; export const projectsApi = { list: (params?: { page?: number; limit?: number; status?: string }) => apiClient.get>('/projects', { params }).then((r) => r.data), get: (id: string) => apiClient.get(`/projects/${id}`).then((r) => r.data), create: (data: CreateProjectPayload) => apiClient.post('/projects', data).then((r) => r.data), update: (id: string, data: Partial) => apiClient.patch(`/projects/${id}`, data).then((r) => r.data), delete: (id: string) => apiClient.delete(`/projects/${id}`).then((r) => r.data), generateScript: (id: string) => apiClient.post(`/projects/${id}/generate-script`).then((r) => r.data), approveAndQueue: (id: string, data?: { ttsProvider?: string; visualEffect?: string }) => apiClient.post<{ projectId: string; renderJobId: string; bullJobId: string }>( `/projects/${id}/approve`, data || {} ).then((r) => r.data), cancelRender: (id: string) => apiClient.post<{ message: string; projectId: string; renderJobId: string; status: string }>( `/projects/${id}/cancel-render`, ).then((r) => r.data), generateSeoTitles: (id: string) => apiClient.post<{ titles: string[]; seoScore: number; currentTitle: string }>( `/projects/${id}/generate-seo-titles`, ).then((r) => r.data), generateSocialContent: (id: string) => apiClient.post(`/projects/${id}/generate-social-content`).then((r) => r.data), selectSeoTitle: (id: string, title: string) => apiClient.patch( `/projects/${id}/select-title`, { title }, ).then((r) => r.data), getRenderQueue: () => apiClient.get('/projects/render-queue').then((r) => r.data), createFromTweet: (data: CreateFromTweetPayload) => apiClient.post('/projects/from-tweet', data).then((r) => r.data), createFromYoutube: (data: CreateFromYoutubePayload) => apiClient.post('/projects/from-youtube', data).then((r) => r.data), createFromDocument: (data: CreateFromDocumentPayload) => { const formData = new FormData(); formData.append('file', data.file); if (data.title) formData.append('title', data.title); if (data.language) formData.append('language', data.language); if (data.aspectRatio) formData.append('aspectRatio', data.aspectRatio); if (data.videoStyle) formData.append('videoStyle', data.videoStyle); if (data.targetDuration) formData.append('targetDuration', data.targetDuration.toString()); return apiClient.post('/projects/from-document', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }).then((r) => r.data); }, createFromText: (data: CreateFromTextPayload) => apiClient.post('/projects/from-text', data).then((r) => r.data), extractDocumentTopics: (data: ExtractDocumentTopicsPayload) => { const formData = new FormData(); formData.append('file', data.file); return apiClient.post('/projects/extract-document-topics', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }).then((r) => r.data); }, createFromExtractedText: (data: CreateFromExtractedTextPayload) => apiClient.post('/projects/document-from-topic', data).then((r) => r.data), updateScene: (projectId: string, sceneId: string, data: Partial) => apiClient.patch(`/projects/${projectId}/scenes/${sceneId}`, data).then((r) => r.data), regenerateScene: (projectId: string, sceneId: string) => apiClient.post(`/projects/${projectId}/scenes/${sceneId}/regenerate`).then((r) => r.data), generateSceneImage: (projectId: string, sceneId: string, customPrompt?: string) => apiClient.post(`/projects/${projectId}/scenes/${sceneId}/generate-image`, { customPrompt }).then((r) => r.data), upscaleSceneImage: (projectId: string, sceneId: string) => apiClient.post(`/projects/${projectId}/scenes/${sceneId}/upscale-image`).then((r) => r.data), }; export const toolsApi = { analyzeYoutubeVideo: (url: string) => apiClient.post('/youtube-tools/analyze', { url }).then((r) => r.data), getYoutubeAnalysisHistory: () => apiClient.get('/youtube-tools/history').then((r) => r.data), getYoutubeAnalysisById: (id: string) => apiClient.get(`/youtube-tools/analyze/${id}`).then((r) => r.data), // SEO analyzeYoutubeSEO: (url: string) => apiClient.post('/youtube-tools/seo/analyze', { url }).then((r) => r.data), getYoutubeSeoHistory: () => apiClient.get('/youtube-tools/seo/history').then((r) => r.data), getYoutubeSeoAnalysisById: (id: string) => apiClient.get(`/youtube-tools/seo/analyze/${id}`).then((r) => r.data), generateYoutubeSeoImage: (prompt: string) => apiClient.post<{ url: string }>('/youtube-tools/seo/generate-image', { prompt }).then((r) => r.data), }; // Backend path: /billing/credits/balance (billing controller prefix) export const creditsApi = { getBalance: () => apiClient.get('/billing/credits/balance').then((r) => r.data), getHistory: (params?: { page?: number; limit?: number }) => apiClient.get('/billing/credits/history', { params }).then((r) => r.data), }; export const billingApi = { createCheckout: (planName: string, billingCycle: 'monthly' | 'yearly') => apiClient.post<{ sessionId: string; url: string }>('/billing/checkout', { planName, billingCycle }).then((r) => r.data), getSubscription: () => apiClient.get('/billing/subscription').then((r) => r.data), getPlans: () => apiClient.get('/billing/plans').then((r) => r.data), }; export const usersApi = { getMe: () => apiClient.get('/users/me').then((r) => r.data), updateProfile: (data: { firstName?: string; lastName?: string }) => apiClient.patch('/users/me', data).then((r) => r.data), changePassword: (data: { currentPassword: string; newPassword: string }) => apiClient.patch('/users/me/password', data).then((r) => r.data), }; export const templatesApi = { list: (params?: { category?: string; language?: string; page?: number; limit?: number }) => apiClient.get>('/templates', { params }).then((r) => r.data), get: (id: string) => apiClient.get