import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { AppLayout } from '../../components/layout/AppLayout';
import { leadGenApi, type OptimizationRule, type OptimizationLog } from '../../api/leadGen';
import { useAuth } from '../../contexts/AuthContext';
import {
ArrowLeft,
Loader2,
Plus,
Play,
Settings2,
AlertCircle,
Activity,
TrendingUp,
Clock,
CheckCircle2,
XCircle,
Zap,
Trash2,
Eye,
} from 'lucide-react';
const RULE_TYPES = [
{ value: 'pause_low_ctr_ad', label: 'Pause Low CTR Ad', description: 'Auto-pause ads with CTR below threshold', target_level: 'ad', default_params: { min_ctr: 0.01, days: 7 } },
{ value: 'scale_winning_ad', label: 'Scale Winning Ad', description: 'Increase budget on high-converting ads', target_level: 'ad', default_params: { min_conversions: 3, budget_increase_pct: 0.25 } },
{ value: 'kill_negative_keywords', label: 'Kill Negative Keywords', description: 'Add low-performing keywords to negatives', target_level: 'keyword', default_params: { min_clicks: 10, max_ctr: 0.005 } },
{ value: 'adjust_bid_up', label: 'Adjust Bid Up', description: 'Raise bids on high-ROAS campaigns', target_level: 'campaign', default_params: { min_roas: 3.0, bid_increase_pct: 0.15 } },
{ value: 'adjust_bid_down', label: 'Adjust Bid Down', description: 'Lower bids on campaigns exceeding CPA target', target_level: 'campaign', default_params: { max_cpa: 50, bid_decrease_pct: 0.2 } },
];
export function OptimizationCenterPage() {
const navigate = useNavigate();
const { user } = useAuth();
const companyId = user?.company_id;
const queryClient = useQueryClient();
const [showNewRule, setShowNewRule] = useState(false);
const [showLogs, setShowLogs] = useState(false);
const { data: rulesData, isLoading: rulesLoading } = useQuery({
queryKey: ['lead-gen-rules', companyId],
queryFn: () => leadGenApi.getRules(companyId!),
enabled: !!companyId,
});
const { data: logData } = useQuery({
queryKey: ['lead-gen-log', companyId, 50],
queryFn: () => leadGenApi.getOptimizationLog(companyId!, 50, 30),
enabled: !!companyId && showLogs,
});
const createRuleMutation = useMutation({
mutationFn: (payload: Parameters<typeof leadGenApi.createRule>[1]) =>
leadGenApi.createRule(companyId!, payload),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['lead-gen-rules'] });
setShowNewRule(false);
},
});
const toggleRuleMutation = useMutation({
mutationFn: ({ ruleId, is_active }: { ruleId: string; is_active: boolean }) =>
leadGenApi.toggleRuleActive(companyId!, ruleId, is_active),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['lead-gen-rules'] }),
});
const runRuleMutation = useMutation({
mutationFn: (ruleId: string) => leadGenApi.runRule(companyId!, ruleId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['lead-gen-rules'] });
queryClient.invalidateQueries({ queryKey: ['lead-gen-log'] });
},
});
const rules = rulesData?.data?.data?.rules || [];
const logs = logData?.data?.data?.logs || [];
const activeRules = rules.filter(r => r.is_active).length;
const totalActions = rules.reduce((s, r) => s + r.total_actions, 0);
return (
<AppLayout title="Optimization Center">
<div className="max-w-6xl mx-auto">
{/* Header */}
<div className="flex items-center justify-between mb-8">
<div className="flex items-center gap-4">
<button
type="button"
onClick={() => navigate('/app/lead-gen')}
className="p-2 hover:bg-surface-100 rounded-lg transition-colors"
>
<ArrowLeft className="h-5 w-5" />
</button>
<div>
<h1 className="text-2xl font-bold text-surface-900">Optimization Center</h1>
<p className="text-sm text-surface-500">
Automated rules for campaign optimization
</p>
</div>
</div>
<button
type="button"
onClick={() => setShowNewRule(true)}
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-brand-600 text-white font-medium text-sm hover:bg-brand-700 transition-colors"
>
<Plus className="h-4 w-4" />
New Rule
</button>
</div>
{/* Summary */}
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-8">
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 mb-2">
<Settings2 className="h-4 w-4 text-blue-600" />
<span className="text-xs font-medium uppercase tracking-wider text-surface-400">Total Rules</span>
</div>
<p className="text-xl font-bold text-surface-900">{rules.length}</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 mb-2">
<Zap className="h-4 w-4 text-emerald-600" />
<span className="text-xs font-medium uppercase tracking-wider text-surface-400">Active</span>
</div>
<p className="text-xl font-bold text-emerald-600">{activeRules}</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 mb-2">
<Activity className="h-4 w-4 text-purple-600" />
<span className="text-xs font-medium uppercase tracking-wider text-surface-400">Total Actions</span>
</div>
<p className="text-xl font-bold text-purple-600">{totalActions}</p>
</div>
</div>
{/* New Rule Form */}
{showNewRule && (
<NewRuleForm
onClose={() => setShowNewRule(false)}
onSubmit={createRuleMutation.mutate}
isPending={createRuleMutation.isPending}
/>
)}
{/* Rules Table */}
{rulesLoading ? (
<div className="flex justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-brand-500" />
</div>
) : rules.length === 0 ? (
<div className="rounded-xl border border-surface-200 bg-white p-12 text-center">
<Settings2 className="h-8 w-8 mx-auto mb-3 text-surface-300" />
<p className="text-surface-500 font-medium">No optimization rules yet</p>
<p className="text-sm text-surface-400 mt-1">
Create a rule to automate campaign adjustments based on performance.
</p>
</div>
) : (
<div className="rounded-xl border border-surface-200 bg-white overflow-hidden mb-8">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-surface-200 bg-surface-50">
<th className="text-left px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Name</th>
<th className="text-left px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Type</th>
<th className="text-left px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Level</th>
<th className="text-center px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Status</th>
<th className="text-right px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Actions</th>
<th className="text-right px-6 py-3 text-xs font-medium uppercase tracking-wider text-surface-500">Last Run</th>
<th className="px-6 py-3 w-28"></th>
</tr>
</thead>
<tbody>
{rules.map(rule => (
<tr key={rule.id} className="border-b border-surface-100 last:border-b-0">
<td className="px-6 py-4">
<span className="font-medium text-surface-900">{rule.name}</span>
</td>
<td className="px-6 py-4">
<span className="inline-flex items-center rounded-full bg-surface-100 px-2 py-0.5 text-xs font-medium text-surface-600 capitalize">
{rule.rule_type.replace(/_/g, ' ')}
</span>
</td>
<td className="px-6 py-4">
<span className="text-xs text-surface-500 capitalize">{rule.target_level}</span>
</td>
<td className="px-6 py-4 text-center">
<button
type="button"
onClick={() => toggleRuleMutation.mutate({ ruleId: rule.id, is_active: !rule.is_active })}
className={`inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium transition-colors ${
rule.is_active
? 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200'
: 'bg-surface-100 text-surface-500 hover:bg-surface-200'
}`}
>
{rule.is_active ? (
<><CheckCircle2 className="h-3 w-3 mr-1" /> Active</>
) : (
<><XCircle className="h-3 w-3 mr-1" /> Paused</>
)}
</button>
</td>
<td className="px-6 py-4 text-right tabular-nums text-surface-700">
{rule.total_actions}
</td>
<td className="px-6 py-4 text-right tabular-nums text-surface-400">
{rule.last_run_at ? (
<span className="flex items-center gap-1 justify-end">
<Clock className="h-3 w-3" />
{new Date(rule.last_run_at).toLocaleDateString()}
</span>
) : (
'Never'
)}
</td>
<td className="px-6 py-4">
<button
type="button"
onClick={() => runRuleMutation.mutate(rule.id)}
disabled={!rule.is_active || runRuleMutation.isPending}
className="flex items-center gap-1 rounded-lg px-2.5 py-1.5 text-xs font-medium text-brand-600 hover:bg-brand-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<Play className="h-3 w-3" />
Run
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* Optimization Log Toggle */}
<button
type="button"
onClick={() => setShowLogs(!showLogs)}
className="flex items-center gap-2 text-sm font-medium text-surface-600 hover:text-surface-800 mb-4"
>
<Activity className="h-4 w-4" />
{showLogs ? 'Hide' : 'Show'} Optimization Log ({logs.length} entries)
</button>
{/* Optimization Log */}
{showLogs && (
<div className="rounded-xl border border-surface-200 bg-white overflow-hidden">
<div className="border-b border-surface-200 px-6 py-4">
<h3 className="font-semibold text-surface-900">Optimization Log</h3>
<p className="text-sm text-surface-500">Recent automated actions</p>
</div>
{logs.length === 0 ? (
<div className="px-6 py-12 text-center text-surface-400">
<Activity className="h-8 w-8 mx-auto mb-3 opacity-50" />
<p className="text-sm">No optimization actions yet.</p>
</div>
) : (
<div className="divide-y divide-surface-100 max-h-[400px] overflow-y-auto">
{logs.map(log => (
<div key={log.id} className="px-6 py-4 flex items-start gap-4">
<div className={`mt-0.5 shrink-0 h-2 w-2 rounded-full ${
log.status === 'success' ? 'bg-emerald-500' :
log.status === 'failed' ? 'bg-red-500' :
'bg-amber-500'
}`} />
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-medium text-sm text-surface-900">{log.target_name}</span>
<span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${
log.action === 'pause'
? 'bg-red-100 text-red-700'
: log.action === 'scale'
? 'bg-emerald-100 text-emerald-700'
: 'bg-blue-100 text-blue-700'
}`}>
{log.action}
</span>
<span className="text-xs text-surface-400 capitalize">
{log.source_service.replace('_', ' ')}
</span>
</div>
{log.reason && (
<p className="text-xs text-surface-500">{log.reason}</p>
)}
</div>
<time className="text-xs text-surface-400 shrink-0 whitespace-nowrap">
{new Date(log.created_at).toLocaleDateString()} {new Date(log.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</time>
</div>
))}
</div>
)}
</div>
)}
</div>
</AppLayout>
);
}
function NewRuleForm({
onClose,
onSubmit,
isPending,
}: {
onClose: () => void;
onSubmit: (payload: { name: string; rule_type: string; target_level?: string; params: Record<string, unknown>; is_active?: boolean }) => void;
isPending: boolean;
}) {
const [selectedType, setSelectedType] = useState('pause_low_ctr_ad');
const [name, setName] = useState('');
const selected = RULE_TYPES.find(t => t.value === selectedType)!;
const [params, setParams] = useState<Record<string, unknown>>(selected.default_params);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!name.trim()) return;
onSubmit({
name: name.trim(),
rule_type: selected.value,
target_level: selected.target_level,
params,
is_active: true,
});
};
return (
<div className="rounded-xl border border-surface-200 bg-white p-6 mb-8">
<h2 className="font-semibold text-surface-900 mb-4">Create Optimization Rule</h2>
<form onSubmit={handleSubmit} className="space-y-4">
{/* Rule type */}
<div>
<label className="block text-sm font-medium text-surface-700 mb-1">Rule Type</label>
<select
value={selectedType}
onChange={e => {
setSelectedType(e.target.value);
const s = RULE_TYPES.find(t => t.value === e.target.value)!;
setParams(s.default_params);
}}
className="w-full sm:w-80 rounded-lg border border-surface-200 px-3 py-2 text-sm focus:border-brand-500"
>
{RULE_TYPES.map(t => (
<option key={t.value} value={t.value}>{t.label}</option>
))}
</select>
{selected && (
<p className="text-xs text-surface-400 mt-1">{selected.description}</p>
)}
</div>
{/* Name */}
<div>
<label className="block text-sm font-medium text-surface-700 mb-1">Rule Name</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder={`e.g. ${selected.label} — Campaign`}
className="w-full sm:w-80 rounded-lg border border-surface-200 px-3 py-2 text-sm focus:border-brand-500"
/>
</div>
{/* Params */}
<div>
<label className="block text-sm font-medium text-surface-700 mb-2">Parameters</label>
<div className="grid grid-cols-2 gap-3">
{Object.entries(params).map(([key, value]) => (
<div key={key}>
<label className="block text-xs text-surface-500 mb-1 capitalize">{key.replace(/_/g, ' ')}</label>
<input
type="number"
step="any"
value={value as number}
onChange={e => setParams(prev => ({ ...prev, [key]: Number(e.target.value) }))}
className="w-full rounded-lg border border-surface-200 px-3 py-2 text-sm focus:border-brand-500"
/>
</div>
))}
</div>
</div>
<div className="flex items-center gap-3 pt-2">
<button
type="submit"
disabled={!name.trim() || isPending}
className="px-4 py-2 rounded-lg bg-brand-600 text-white font-medium text-sm hover:bg-brand-700 disabled:opacity-50 transition-colors"
>
{isPending ? 'Creating...' : 'Create Rule'}
</button>
<button
type="button"
onClick={onClose}
className="px-4 py-2 rounded-lg text-sm font-medium text-surface-600 hover:bg-surface-100"
>
Cancel
</button>
</div>
</form>
</div>
);
}