"use client"; import { useState, useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import useStore from "@/lib/store"; import modelAdapter from "@/lib/services/adapter-instance"; import { Megaphone, Copy, Loader2, CheckCircle2, Settings, Plus, X, ChevronDown, ChevronUp, Wand2, Target, TrendingUp, ShieldAlert, BarChart3, Users, Rocket } from "lucide-react"; import { cn } from "@/lib/utils"; import { GoogleAdsResult } from "@/types"; import { translations } from "@/lib/i18n/translations"; export default function GoogleAdsGenerator() { const { googleAdsResult, magicWandResult, selectedProvider, selectedModels, availableModels, apiKeys, isProcessing, error, language, setGoogleAdsResult, setMagicWandResult, setProcessing, setError, setAvailableModels, setSelectedModel, setSelectedProvider, } = useStore(); const t = translations[language].googleAds; const common = translations[language].common; // Input states const [websiteUrl, setWebsiteUrl] = useState(""); const [products, setProducts] = useState<{ name: string; url: string }[]>([{ name: "", url: "" }]); const [targetAudience, setTargetAudience] = useState(""); const [budgetMin, setBudgetMin] = useState("500"); const [budgetMax, setBudgetMax] = useState("2000"); const [duration, setDuration] = useState("30 days"); const [industry, setIndustry] = useState(""); const [copied, setCopied] = useState(false); const [expandedSections, setExpandedSections] = useState(["keywords"]); const [isMagicThinking, setIsMagicThinking] = useState(false); const [progressMessage, setProgressMessage] = useState(""); const [progressIndex, setProgressIndex] = useState(0); const selectedModel = selectedModels[selectedProvider]; const models = availableModels[selectedProvider] || modelAdapter.getAvailableModels(selectedProvider); // Fun progress messages const progressMessages = language === "ru" ? [ "🔍 Изучаю ваш сайт...", "🧠 Анализирую конкурентов...", "💡 Генерирую гениальные идеи...", "📊 Исследую рыночные тренды...", "🎯 Определяю целевую аудиторию...", "✨ Создаю магию рекламы...", "🚀 Почти готово, потерпите...", "📝 Пишу убедительные тексты...", "🔥 Оптимизирую для конверсий..." ] : language === "he" ? [ "🔍 בודק את האתר שלך...", "🧠 מנתח מתחרים...", "💡 מייצר רעיונות גאוניים...", "📊 חוקר מגמות שוק...", "🎯 מזהה קהל יעד...", "✨ יוצר קסם פרסום...", "🚀 כמעט שם, רק רגע...", "📝 כותב טקסטים משכנעים...", "🔥 מייעל להמרות..." ] : [ "🔍 Studying your website...", "🧠 Analyzing competitors...", "💡 Generating brilliant ideas...", "📊 Researching market trends...", "🎯 Identifying target audience...", "✨ Creating advertising magic...", "🚀 Almost there, hang tight...", "📝 Writing persuasive copy...", "🔥 Optimizing for conversions..." ]; const toggleSection = (section: string) => { setExpandedSections((prev) => prev.includes(section) ? prev.filter((s) => s !== section) : [...prev, section] ); }; useEffect(() => { if (typeof window !== "undefined") { loadAvailableModels(); const saved = localStorage.getItem("promptarch-api-keys"); if (saved) { try { const keys = JSON.parse(saved); if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen); if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama); if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai); } catch (e) { console.error("Failed to load API keys:", e); } } } }, [selectedProvider]); // Cycle through progress messages while generating useEffect(() => { if (isProcessing || isMagicThinking) { setProgressMessage(progressMessages[0]); setProgressIndex(0); const interval = setInterval(() => { setProgressIndex(prev => { const nextIndex = (prev + 1) % progressMessages.length; setProgressMessage(progressMessages[nextIndex]); return nextIndex; }); }, 2500); return () => clearInterval(interval); } else { setProgressMessage(""); setProgressIndex(0); } }, [isProcessing, isMagicThinking, language]); const loadAvailableModels = async () => { const fallbackModels = modelAdapter.getAvailableModels(selectedProvider); setAvailableModels(selectedProvider, fallbackModels); try { const result = await modelAdapter.listModels(selectedProvider); if (result.success && result.data) { setAvailableModels(selectedProvider, result.data[selectedProvider] || fallbackModels); } } catch (error) { console.error("Failed to load models:", error); } }; const addProduct = () => setProducts([...products, { name: "", url: "" }]); const removeProduct = (index: number) => { const newProducts = products.filter((_, i) => i !== index); setProducts(newProducts.length ? newProducts : [{ name: "", url: "" }]); }; const updateProduct = (index: number, field: "name" | "url", value: string) => { const newProducts = [...products]; newProducts[index] = { ...newProducts[index], [field]: value }; setProducts(newProducts); }; const handleGenerate = async () => { if (!websiteUrl.trim()) { setError("Please enter a website URL"); return; } const filteredProducts = products.filter(p => p.name.trim() !== ""); if (filteredProducts.length === 0) { setError(language === "ru" ? "Добавьте хотя бы один продукт или услугу" : language === "he" ? "הוסף לפחות מוצר או שירות אחד" : "Please add at least one product or service"); return; } const apiKey = apiKeys[selectedProvider]; const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } setProcessing(true); setError(null); setMagicWandResult(null); console.log("[GoogleAdsGenerator] Starting generation...", { selectedProvider, selectedModel }); try { // Convert products to strings with optional URLs for AI context const productStrings = filteredProducts.map(p => p.url ? `${p.name} (URL: ${p.url})` : p.name ); const result = await modelAdapter.generateGoogleAds(websiteUrl, { productsServices: productStrings, targetAudience, budgetRange: { min: parseInt(budgetMin), max: parseInt(budgetMax), currency: "USD" }, campaignDuration: duration, industry, language: "English" }, selectedProvider, selectedModel); console.log("[GoogleAdsGenerator] Generation result:", result); if (result.success && result.data) { try { // Robust JSON extraction const extractJson = (text: string) => { try { return JSON.parse(text); } catch (e) { const jsonMatch = text.match(/```json\s*([\s\S]*?)\s*```/i) || text.match(/```\s*([\s\S]*?)\s*```/i); if (jsonMatch && jsonMatch[1]) { try { return JSON.parse(jsonMatch[1].trim()); } catch (e2) { /* ignore */ } } const braceMatch = text.match(/(\{[\s\S]*\})/); if (braceMatch) { try { return JSON.parse(braceMatch[0].trim()); } catch (e3) { /* ignore */ } } throw new Error("Could not parse JSON from response"); } }; const rawData = typeof result.data === 'string' ? result.data : JSON.stringify(result.data); const parsedData = extractJson(rawData); const adsResult: GoogleAdsResult = { ...parsedData, id: Math.random().toString(36).substr(2, 9), websiteUrl, productsServices: filteredProducts, generatedAt: new Date(), rawContent: rawData }; setGoogleAdsResult(adsResult); setExpandedSections(["keywords"]); } catch (e) { console.error("Failed to parse ads data:", e); setError("Failed to parse the generated ads content. Please try again."); } } else { console.error("[GoogleAdsGenerator] Generation failed:", result.error); setError(result.error || "Failed to generate Google Ads campaign"); } } catch (err) { console.error("[GoogleAdsGenerator] Generation error:", err); setError(err instanceof Error ? err.message : "An error occurred"); } finally { setProcessing(false); } }; const handleMagicWand = async () => { if (!websiteUrl.trim()) { setError("Please enter a website URL"); return; } const firstProduct = products.find(p => p.name.trim() !== ""); if (!firstProduct) { setError(language === "ru" ? "Добавьте хотя бы один продукт" : language === "he" ? "הוסף לפחות מוצר אחד" : "Please add at least one product to promote"); return; } const apiKey = apiKeys[selectedProvider]; const isQwenOAuth = selectedProvider === "qwen" && modelAdapter.hasQwenAuth(); if (!isQwenOAuth && (!apiKey || !apiKey.trim())) { setError(`Please configure your ${selectedProvider.toUpperCase()} API key in Settings`); return; } setIsMagicThinking(true); setError(null); setGoogleAdsResult(null); try { // Pass product with URL for enhanced AI research const productString = firstProduct.url ? `${firstProduct.name} (Product URL for research: ${firstProduct.url})` : firstProduct.name; const result = await modelAdapter.generateMagicWand( websiteUrl, productString, parseInt(budgetMax), selectedProvider, selectedModel ); if (result.success && result.data) { const extractJson = (text: string) => { try { return JSON.parse(text); } catch (e) { const jsonMatch = text.match(/```json\s*([\s\S]*?)\s*```/i) || text.match(/```\s*([\s\S]*?)\s*```/i); if (jsonMatch) return JSON.parse(jsonMatch[1].trim()); const braceMatch = text.match(/(\{[\s\S]*\})/); if (braceMatch) return JSON.parse(braceMatch[0].trim()); throw e; } }; const data = extractJson(result.data); setMagicWandResult({ ...data, id: Math.random().toString(36).substr(2, 9), websiteUrl, product: firstProduct, budget: parseInt(budgetMax), generatedAt: new Date(), rawContent: result.data }); setExpandedSections(["market", "strategies"]); } else { setError(result.error || "Magic Wand failed to research the market"); } } catch (err) { setError(err instanceof Error ? err.message : "An error occurred during Magic Wand research"); } finally { setIsMagicThinking(false); } }; const handleCopy = async () => { const content = googleAdsResult?.rawContent || magicWandResult?.rawContent; if (content) { await navigator.clipboard.writeText(content); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const sections = [ { id: "keywords", title: language === "ru" ? "Исследование ключевых слов" : language === "he" ? "מחקר מילות מפתח" : "Keywords Research" }, { id: "adcopies", title: language === "ru" ? "Варианты объявлений" : language === "he" ? "גרסאות עותקי מודעות" : "Ad Copy Variations" }, { id: "campaigns", title: language === "ru" ? "Структура кампании" : language === "he" ? "מבנה קמפיין" : "Campaign Structure" }, { id: "implementation", title: language === "ru" ? "Руководство по внедрению" : language === "he" ? "מדריך יישום" : "Implementation Guide" }, ]; const renderSectionContent = (sectionId: string) => { if (!googleAdsResult) return null; switch (sectionId) { case "keywords": return (
{googleAdsResult.keywords?.primary?.length > 0 && (

Primary Keywords

{googleAdsResult.keywords.primary.map((k, i) => ( {k.keyword} {k.cpc && ({k.cpc})} ))}
)} {googleAdsResult.keywords?.longTail?.length > 0 && (

Long-Tail Keywords

{googleAdsResult.keywords.longTail.map((k, i) => ( {k.keyword} ))}
)} {googleAdsResult.keywords?.negative?.length > 0 && (

Negative Keywords

{googleAdsResult.keywords.negative.map((k, i) => ( {k.keyword} ))}
)}
); case "adcopies": return (
{googleAdsResult.adCopies?.map((ad, i) => (
Ad Variation {i + 1}
{ad.headlines?.map((h, j) => (
{h}
))}
{ad.descriptions?.map((d, j) => (

{d}

))}
))}
); case "campaigns": return (
{googleAdsResult.campaigns?.map((camp, i) => (
{camp.name}
{camp.type}
{camp.budget && (
${camp.budget.monthly}/mo
${camp.budget.daily}/day
)}
{camp.adGroups?.length > 0 && (
Ad Groups
{camp.adGroups.map((g, j) => ( {g.name} ))}
)}
))}
); case "implementation": return (
{googleAdsResult.implementation?.setupSteps?.length > 0 && (

Setup Steps

    {googleAdsResult.implementation.setupSteps.map((step, i) => (
  1. {step}
  2. ))}
)} {googleAdsResult.implementation?.qualityScoreTips?.length > 0 && (

Quality Score Tips

    {googleAdsResult.implementation.qualityScoreTips.map((tip, i) => (
  • {tip}
  • ))}
)}
); default: return
{googleAdsResult.rawContent}
; } }; const renderMagicWandSectionContent = (sectionId: string) => { if (!magicWandResult) return null; switch (sectionId) { case "market": return (
Industry Size
{magicWandResult.marketAnalysis.industrySize}
Growth Rate
{magicWandResult.marketAnalysis.growthRate}

Market Leaders

{magicWandResult.marketAnalysis.topCompetitors.map((c, i) => ( {c} ))}

Emerging Trends

    {magicWandResult.marketAnalysis.marketTrends.map((t, i) => (
  • {t}
  • ))}
); case "competitors": return (
{magicWandResult.competitorInsights.map((comp, i) => (

{comp.competitor}

Strengths
    {comp.strengths.map((s, j) => (
  • {s}
  • ))}
Weaknesses
    {comp.weaknesses.map((w, j) => (
  • {w}
  • ))}
Spy Report: {comp.adStrategy}
))}
); case "strategies": return (
{magicWandResult.strategies.map((strat, i) => (

{strat.direction}

{strat.targetAudience}

{strat.riskLevel} risk {strat.timeToResults} to results

The "Why": {strat.rationale}

Edge: {strat.competitiveAdvantage}
{strat.keyMessages.map((msg, j) => ( {msg} ))}
Channel Mix
{strat.recommendedChannels.map((c, j) => ( {c} ))}
Expected ROI
{strat.expectedROI}
))}
); default: return
{magicWandResult.rawContent}
; } }; return (
{t.title} {t.description}
{(["qwen", "ollama", "zai"] as const).map((provider) => ( ))}
setWebsiteUrl(e.target.value)} className="text-sm" />
{products.map((product, index) => (
updateProduct(index, "name", e.target.value)} className="text-sm" /> {products.length > 1 && ( )}
updateProduct(index, "url", e.target.value)} className="text-xs text-muted-foreground" />
))}
setBudgetMin(e.target.value)} className="text-sm" /> - setBudgetMax(e.target.value)} className="text-sm" />
setIndustry(e.target.value)} className="text-sm" />