'use client'
import React, { useState, useEffect, useRef } from 'react'
import { useRouter } from 'next/navigation'
import { useAuth } from '@/components/AuthProvider'
import { MessageUI } from '@/lib/types/database'
import { DEFAULT_MODEL } from '@/lib/config/models'
import ChatMessages from '@/components/chat/ChatMessages'
import ModelSelector from '@/components/ModelSelector'
import { IoSendOutline, IoStopOutline, IoSparklesOutline, IoBulbOutline, IoRocketOutline } from 'react-icons/io5'
import { useChatContext } from '@/lib/contexts/ChatContext'
import { useUsageLimit } from '@/lib/hooks/useUsageLimit'
import { deleteMessage } from '@/lib/utils/chatMessageUtils'
export default function NewChatPage() {
const router = useRouter()
const { setCurrentChatId, addNewChatToList } = useChatContext()
const { user } = useAuth()
const [messages, setMessages] = useState<MessageUI[]>([])
const [inputValue, setInputValue] = useState('')
const [isStreaming, setIsStreaming] = useState(false)
const [selectedModel, setSelectedModel] = useState(DEFAULT_MODEL.name)
const abortControllerRef = useRef<AbortController | null>(null)
const { usageInfo } = useUsageLimit(user?.id || null)
// Rotating tips for inspiration
const tips = [
{
icon: IoSparklesOutline,
title: "Brainstorm Ideas",
description: "Help me develop plot twists, character arcs, or world-building elements for your story."
},
{
icon: IoBulbOutline,
title: "Writing Feedback",
description: "Get constructive feedback on your writing style, pacing, or dialogue."
},
{
icon: IoRocketOutline,
title: "Creative Prompts",
description: "Ask for writing prompts, scene suggestions, or creative challenges to overcome writer's block."
},
{
icon: IoSparklesOutline,
title: "Research Assistant",
description: "Help me research historical facts, scientific concepts, or cultural details for your writing."
},
{
icon: IoBulbOutline,
title: "Character Development",
description: "Create detailed character profiles, backstories, or personality traits."
},
{
icon: IoRocketOutline,
title: "Plot Structure",
description: "Help me outline chapters, plan story arcs, or organize plot points."
}
]
const [currentTipIndex, setCurrentTipIndex] = useState(0)
// Rotate tips every 4 seconds
useEffect(() => {
const interval = setInterval(() => {
setCurrentTipIndex((prev) => (prev + 1) % tips.length)
}, 4000)
return () => clearInterval(interval)
}, [tips.length])
// Clear current chat when on new chat page
useEffect(() => {
setCurrentChatId(null)
}, [setCurrentChatId])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!inputValue.trim() || !user?.id || isStreaming) return
const userMessage = inputValue.trim()
setInputValue('')
setIsStreaming(true)
// Add user message to UI immediately
const tempUserMessage: MessageUI = {
id: `temp-${Date.now()}`,
type: 'user',
content: userMessage,
timestamp: new Date()
}
setMessages([tempUserMessage])
try {
abortControllerRef.current = new AbortController()
const response = await fetch('/api/standalone-chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [tempUserMessage],
model: selectedModel,
userId: user?.id,
chatId: null // New chat
}),
signal: abortControllerRef.current.signal
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to send message')
}
// Handle streaming response
const reader = response.body?.getReader()
if (!reader) throw new Error('No response body')
const decoder = new TextDecoder()
let aiResponse = ''
let newChatId: string | null = null
const tempAiMessage: MessageUI & { _isStreaming?: boolean } = {
id: `temp-ai-${Date.now()}`,
type: 'ai',
content: '',
timestamp: new Date(),
model: selectedModel,
_isStreaming: true
}
setMessages(prev => [...prev, tempAiMessage])
while (true) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value, { stream: true })
const lines = chunk.split('\n')
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6))
if (data.type === 'content') {
aiResponse += data.content
setMessages(prev => prev.map(msg =>
msg.id === tempAiMessage.id
? { ...msg, content: aiResponse }
: msg
))
} else if (data.type === 'done') {
newChatId = data.chatId
// Fetch the new chat data and add it to sidebar before redirecting
if (newChatId && addNewChatToList) {
try {
const { supabase } = await import('@/lib/supabase')
const { data: newChatData, error } = await supabase
.from('chats')
.select('*')
.eq('id', newChatId)
.single()
if (!error && newChatData) {
// Add the new chat to the sidebar
addNewChatToList(newChatData)
}
} catch (error) {
console.error('Error fetching new chat data:', error)
}
}
// Redirect to the new chat page
if (newChatId) {
router.push(`/dashboard/chat/${newChatId}`)
}
} else if (data.type === 'error') {
throw new Error(data.error)
}
} catch (e) {
// Skip invalid JSON
}
}
}
}
// Remove streaming flag
setMessages(prev => prev.map(msg =>
msg.id === tempAiMessage.id
? { ...msg, _isStreaming: false }
: msg
))
} catch (error: any) {
console.error('Error sending message:', error)
if (error.name === 'AbortError') {
// Request was cancelled
setMessages([])
} else {
// Show error message
const errorMessage: MessageUI = {
id: `error-${Date.now()}`,
type: 'ai',
content: `Sorry, I encountered an error: ${error.message}`,
timestamp: new Date()
}
setMessages(prev => [...prev.slice(0, -1), errorMessage])
}
} finally {
setIsStreaming(false)
abortControllerRef.current = null
}
}
const stopExecution = () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort()
}
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSubmit(e as any)
}
}
const getAvatarUrl = () => user?.user_metadata?.avatar_url || null
const getInitials = () => {
const fullName = user?.user_metadata?.full_name
if (fullName) {
return fullName.split(' ').map((n: string) => n[0]).join('').toUpperCase()
}
return user?.email?.[0]?.toUpperCase() || 'U'
}
const getPlaceholder = () => {
if (isStreaming) return "AI is thinking..."
return "Start a new conversation..."
}
const handleDeleteMessage = async (messageId: string) => {
try {
await deleteMessage(messageId)
// Remove the message from the local state
setMessages(prev => prev.filter(msg => msg.id.toString() !== messageId))
} catch (error: any) {
console.error('Error deleting message:', error)
// You could show a toast notification here
throw error // Re-throw to let the Message component handle the error state
}
}
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="border-b border-white/10 bg-black/40 backdrop-blur-sm px-4 py-2 md:pl-4 pl-4">
<h1 className="text-sm font-medium text-slate-300">New Chat</h1>
</div>
{/* Messages Area */}
<div className="flex-1 flex flex-col min-h-0">
<div className="flex-1 overflow-y-auto">
<div className="max-w-4xl mx-auto px-4">
{messages.length === 0 ? (
<div className="flex-1 flex items-center justify-center py-8">
<div className="text-center max-w-2xl mx-auto px-4">
{/* Main heading with gradient */}
<div className="mb-8">
<h2 className="text-3xl font-bold bg-gradient-to-r from-blue-400 via-purple-400 to-pink-400 bg-clip-text text-transparent mb-4">
Your AI Writing Partner
</h2>
<p className="text-slate-300 text-lg leading-relaxed">
Ready to bring your stories to life? Let's create something amazing together.
</p>
</div>
{/* Rotating tip card */}
<div className="mb-8">
<div className="bg-gradient-to-br from-slate-800/50 to-slate-900/50 backdrop-blur-sm border border-slate-700/50 rounded-2xl p-6 transition-all duration-500 h-48 flex flex-col justify-center">
<div className="flex items-center justify-center mb-4">
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-purple-500/20 to-pink-500/20 flex items-center justify-center border border-purple-500/30">
{React.createElement(tips[currentTipIndex].icon, { className: "w-6 h-6 text-purple-400" })}
</div>
</div>
<h3 className="text-lg font-semibold text-white mb-2 text-center">
{tips[currentTipIndex].title}
</h3>
<p className="text-slate-300 text-sm leading-relaxed text-center line-clamp-3">
{tips[currentTipIndex].description}
</p>
</div>
</div>
{/* Quick prompt suggestions */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 mb-8">
<button
onClick={() => setInputValue("Help me brainstorm a unique plot twist for my fantasy novel about a time-traveling librarian.")}
className="p-3 text-left bg-slate-800/30 hover:bg-slate-700/40 border border-slate-700/50 hover:border-slate-600/50 rounded-xl transition-all duration-200 group"
>
<div className="text-xs text-purple-400 font-medium mb-1">💡 Plot Twist</div>
<div className="text-sm text-slate-300 group-hover:text-white transition-colors">
Help me brainstorm a unique plot twist...
</div>
</button>
<button
onClick={() => setInputValue("Create a detailed character profile for a morally ambiguous anti-hero who was once a respected judge.")}
className="p-3 text-left bg-slate-800/30 hover:bg-slate-700/40 border border-slate-700/50 hover:border-slate-600/50 rounded-xl transition-all duration-200 group"
>
<div className="text-xs text-blue-400 font-medium mb-1">👤 Character</div>
<div className="text-sm text-slate-300 group-hover:text-white transition-colors">
Create a detailed character profile...
</div>
</button>
<button
onClick={() => setInputValue("Give me feedback on this dialogue: 'I never asked for this,' she whispered, her voice barely audible over the storm.'")}
className="p-3 text-left bg-slate-800/30 hover:bg-slate-700/40 border border-slate-700/50 hover:border-slate-600/50 rounded-xl transition-all duration-200 group"
>
<div className="text-xs text-emerald-400 font-medium mb-1">✍️ Feedback</div>
<div className="text-sm text-slate-300 group-hover:text-white transition-colors">
Give me feedback on this dialogue...
</div>
</button>
<button
onClick={() => {
setInputValue("Write an explicit sex scene between two characters. Include graphic descriptions, vulgar language, and intimate details. Make it hot and erotic.")
setSelectedModel("Mistral Nemo")
}}
className="p-3 text-left bg-slate-800/30 hover:bg-slate-700/40 border border-slate-700/50 hover:border-slate-600/50 rounded-xl transition-all duration-200 group"
>
<div className="text-xs text-red-400 font-medium mb-1">🔥 NSFW</div>
<div className="text-sm text-slate-300 group-hover:text-white transition-colors">
Write an explicit sex scene...
</div>
</button>
</div>
{/* Inspiration footer */}
<div className="text-slate-500 text-sm">
<span className="inline-flex items-center gap-1">
<IoSparklesOutline className="w-4 h-4" />
Your creativity, amplified by AI
</span>
</div>
</div>
</div>
) : (
<ChatMessages
allDisplayMessages={messages}
isStreaming={isStreaming}
currentChat={null}
contextInfo={null}
toolResults={[]}
getAvatarUrl={getAvatarUrl}
getInitials={getInitials}
onFileClick={async () => {}}
onDeleteMessage={handleDeleteMessage}
/>
)}
</div>
</div>
</div>
{/* Input Area */}
<div className="bg-black/40 backdrop-blur-sm px-4 py-3">
<div className="max-w-4xl mx-auto">
<form onSubmit={handleSubmit}>
<div className="relative bg-white/5 backdrop-blur-sm border border-white/10 rounded-lg transition-all">
<textarea
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value)
e.target.style.height = 'auto'
e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`
}}
onKeyDown={handleKeyDown}
placeholder={getPlaceholder()}
className="w-full bg-transparent text-slate-200 placeholder-slate-400 border-0 outline-none resize-none px-4 py-3 pr-12 pb-12 min-h-[3rem] max-h-[7.5rem]"
disabled={isStreaming}
autoFocus
/>
<div className="absolute left-4 bottom-3 flex items-center gap-2">
<ModelSelector
selectedModel={selectedModel}
onModelChange={setSelectedModel}
disabled={isStreaming}
variant="minimal"
className="min-w-0"
usageInfo={usageInfo}
/>
</div>
<div className="absolute right-2 bottom-3 flex items-center gap-2">
{isStreaming ? (
<button
type="button"
onClick={stopExecution}
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-white/10 rounded-md transition-colors"
title="Stop generation"
>
<IoStopOutline className="h-4 w-4" />
</button>
) : (
<button
type="submit"
disabled={!inputValue.trim() || isStreaming}
className="p-2 text-slate-400 hover:text-slate-200 hover:bg-white/10 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
title="Send message"
>
<IoSendOutline className="h-4 w-4" />
</button>
)}
</div>
</div>
</form>
</div>
</div>
</div>
)
}