"use client"; import { useState, useEffect } from "react"; import type { AnalysisResult, CompanyProfile, Tender, TenderDetailInfo } from "../lib/types"; import { uploadDocument, fetchTenderDetails } from "../lib/api"; import AgentChat from "./AgentChat"; type Props = { tender: Tender | null; companyProfile: CompanyProfile; analysis: AnalysisResult | null; onAnalyze: (documentText?: string, models?: Record, tenderDetails?: TenderDetailInfo | null) => Promise; onBackToSearch: () => void; }; const agents = [ { id: "legal", name: "Dra. Legal", role: "Compliance", avatar: "⚖️", color: "text-amber-400", desc: "Verifies administrative bases and legal risks." }, { id: "tech", name: "Ing. Tech", role: "Architecture", avatar: "👨‍💻", color: "text-cyan", desc: "Evaluates technical feasibility and stack requirements." }, { id: "risk", name: "Sra. Estrategia", role: "ROI & Risk", avatar: "🕵️‍♀️", color: "text-purple-400", desc: "Calculates commercial impact and win probability." }, ]; export default function AgentAnalysis({ tender, companyProfile, analysis, onAnalyze, onBackToSearch }: Props) { const [approved, setApproved] = useState(false); const [isRunning, setIsRunning] = useState(false); const [file, setFile] = useState(null); const [isUploading, setIsUploading] = useState(false); const [documentText, setDocumentText] = useState(""); const [agentModels, setAgentModels] = useState({ legal: "Gemini 2.5 Flash", tech: "DeepSeek-V3.2 (Featherless)", risk: "Qwen-2.5 (Featherless)" }); const [activeSettings, setActiveSettings] = useState(null); const [statusLog, setStatusLog] = useState([]); const [error, setError] = useState(null); const [tenderDetails, setTenderDetails] = useState(null); const [isLoadingDetails, setIsLoadingDetails] = useState(false); // Multiple Files Support (The Corral) const [corral, setCorral] = useState>([]); const [activeAnimalId, setActiveAnimalId] = useState(null); const [generatedAnnexes, setGeneratedAnnexes] = useState>([]); const [isGeneratingAnnexes, setIsGeneratingAnnexes] = useState(false); const [pdfUrls, setPdfUrls] = useState>({}); // Removed auto-scroll to keep user at the top during demo recordings // Fetch Tender Details (Scraped) useEffect(() => { const getDetails = async () => { if (!tender?.code) return; setIsLoadingDetails(true); try { // Try to get details using both code and potential qs (if available in tender object) // Note: For now we use code, if the API returns a qs param we should use it const details = await fetchTenderDetails(tender.code); setTenderDetails(details); } catch (err) { console.error("Failed to fetch tender details:", err); } finally { setIsLoadingDetails(false); } }; getDetails(); }, [tender?.code]); const generateAnnexes = async () => { if (!tender) return; setIsGeneratingAnnexes(true); // Simulate AI generating specific annexes based on tender data setTimeout(() => { const annexes = [ { name: "Anexo 1: Identificación del Oferente", content: `# ANEXO N°1\nIDENTIFICACIÓN DEL OFERENTE\n\n**Licitación:** ${tender.name}\n**ID:** ${tender.code}\n\n**RAZÓN SOCIAL:** ${companyProfile.name}\n**RUT:** 77.345.123-K\n**REPRESENTANTE LEGAL:** Álvaro Pérez\n**DOMICILIO:** Av. Apoquindo 4500, Las Condes, Santiago.\n**GIRO:** ${companyProfile.industry}\n\n*Documento generado automáticamente por AndesOps AI.*` }, { name: "Anexo 2: Declaración Jurada Simple", content: `# ANEXO N°2\nDECLARACIÓN JURADA SIMPLE\n\nYo, Álvaro Pérez, en representación de ${companyProfile.name}, declaro bajo juramento que mi representada no se encuentra afecta a ninguna de las inhabilidades previstas en el artículo 92 de la Ley N° 19.886.\n\n**Fecha:** ${new Date().toLocaleDateString()}\n\n__________________________\nFirma Representante Legal` }, { name: "Anexo 3: Experiencia del Oferente", content: `# ANEXO N°3\nEXPERIENCIA DEL OFERENTE\n\n**Empresa:** ${companyProfile.name}\n**Años de Experiencia:** ${companyProfile.experience}\n\n**Principales Servicios:**\n${companyProfile.services.map(s => `- ${s}`).join('\n')}\n\n**Certificaciones:**\n${companyProfile.certifications.map(c => `- ${c}`).join('\n')}\n\n*Validado por AndesOps AI Intelligence.*` } ]; setGeneratedAnnexes(annexes); setIsGeneratingAnnexes(false); // Smooth scroll to annexes setTimeout(() => { document.getElementById('annexes-section')?.scrollIntoView({ behavior: 'smooth' }); }, 100); }, 2000); }; const downloadAsPDF = async (annex: { name: string, content: string }) => { try { const { jsPDF } = await import("jspdf"); const doc = new jsPDF(); // Title doc.setFontSize(22); doc.setTextColor(40, 40, 40); doc.text("ANDESOPS AI - COMPLIANCE", 20, 20); doc.setDrawColor(168, 85, 247); // Purple line doc.setLineWidth(1); doc.line(20, 25, 190, 25); // Content doc.setFontSize(16); doc.setTextColor(0, 0, 0); doc.text(annex.name, 20, 40); doc.setFontSize(10); doc.setFont("helvetica", "normal"); const splitText = doc.splitTextToSize(annex.content.replace(/# /g, '').replace(/\*\*/g, '').replace(/### /g, ''), 170); doc.text(splitText, 20, 55); // Footer doc.setFontSize(8); doc.setTextColor(150, 150, 150); doc.text(`Document generated by AndesOps AI on ${new Date().toLocaleString()}`, 20, 280); doc.save(`${annex.name.replace(/ /g, '_')}.pdf`); } catch (err) { console.error("PDF Export failed:", err); alert("PDF Export failed. Downloading as Markdown instead."); const blob = new Blob([annex.content], { type: 'text/markdown' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `${annex.name.replace(/ /g, '_')}.md`; a.click(); } }; const handleFileChange = async (event: React.ChangeEvent) => { if (event.target.files && event.target.files.length > 0) { const filesArray = Array.from(event.target.files); setIsUploading(true); setError(null); try { for (const newFile of filesArray) { const id = Math.random().toString(36).substring(7); const uploadResult = await uploadDocument(newFile); const newEntry = { file: newFile, text: uploadResult.text, analysis: null, id }; if (newFile.type === "application/pdf") { const url = URL.createObjectURL(newFile); setPdfUrls(prev => ({ ...prev, [id]: url })); } setCorral(prev => [...prev, newEntry]); setActiveAnimalId(id); } } catch (err) { console.error("Upload error", err); setError("Failed to upload and process one or more documents."); } finally { setIsUploading(false); } } }; const removeFromCorral = (id: string, e: React.MouseEvent) => { e.stopPropagation(); setCorral(prev => prev.filter(a => a.id !== id)); if (activeAnimalId === id) setActiveAnimalId(null); }; const handleAnalyzeClick = async () => { if (!approved || !tender || !activeAnimalId) return; const activeEntry = corral.find(a => a.id === activeAnimalId); if (!activeEntry) return; setIsRunning(true); setError(null); setStatusLog(["🚀 Initializing Agent War Room...", `📡 Focusing on: ${activeEntry.file.name}...`]); try { setStatusLog(prev => [...prev, "🤝 Summoning experts: Legal, Technical, and Strategy..."]); const progressTimer = setInterval(() => { const messages = [ "⚖️ Dra. Legal is reviewing clauses...", "👨‍💻 Ing. Tech is analyzing feasibility...", "🕵️‍♀️ Sra. Estrategia is calculating ROI...", "🧠 Synthesizing consensus..." ]; setStatusLog(prev => { if (prev.length < 10) { const nextMsg = messages[Math.floor(Math.random() * messages.length)]; if (!prev.includes(nextMsg)) return [...prev, nextMsg]; } return prev; }); }, 800); // Faster log timing for snappier feel // We call the parent's onAnalyze but we want the result back locally too // Actually, since we want multiple analyses, we might need to handle the result here // For now, let's assume the parent updates the main analysis prop, but we'll store it in the corral too await onAnalyze(activeEntry.text, agentModels, tenderDetails); clearInterval(progressTimer); setStatusLog(prev => [...prev, "✨ Analysis complete!"]); } catch (err) { console.error("Error during analysis flow:", err); setError("The analysis pipeline encountered a technical failure."); setStatusLog(prev => [...prev, "❌ Error occurred during analysis pipeline."]); } finally { setIsRunning(false); } }; // Sync parent analysis to corral entry useEffect(() => { if (analysis && activeAnimalId) { setCorral(prev => prev.map(a => a.id === activeAnimalId ? { ...a, analysis } : a)); } }, [analysis]); const activeAnalysis = corral.find(a => a.id === activeAnimalId)?.analysis || analysis; const getFileIcon = (fileName: string) => { const ext = fileName.split('.').pop()?.toLowerCase(); if (ext === 'pdf') return { emoji: "📄", label: "PDF", color: "bg-red-500/20 text-red-400 border-red-500/30" }; if (ext === 'doc' || ext === 'docx') return { emoji: "📝", label: "DOC", color: "bg-blue-500/20 text-blue-400 border-blue-500/30" }; if (ext === 'xls' || ext === 'xlsx') return { emoji: "📊", label: "XLS", color: "bg-green-500/20 text-green-400 border-green-500/30" }; if (ext === 'zip' || ext === 'rar') return { emoji: "📦", label: "ZIP", color: "bg-amber-500/20 text-amber-400 border-amber-500/30" }; return { emoji: "📁", label: "FILE", color: "bg-slate-500/20 text-slate-400 border-white/10" }; }; if (!tender && !analysis) { return (
🤖

Agent War Room

Our specialized agents are ready to analyze your next big opportunity. Select a tender from Tender Search to begin.

{agents.map(agent => (
{agent.avatar}

{agent.role}

{agent.name}

{agent.desc}

))}
); } return (
{/* Navigation Header */}
{/* Tender Header Card */}
Active Opportunity {tender?.name.toLowerCase().includes('sustentable') || tender?.description?.toLowerCase().includes('ambiental') ? ( 🌱 Sustainable / Compra Ágil ) : null} {tenderDetails?.metadata?.question_count && tenderDetails.metadata.question_count > 0 ? ( 💬 {tenderDetails.metadata.question_count} Questions ) : null} {tender?.code}

{tender?.name}

{tender?.buyer}

{tender && (

Investment

{tender.estimated_amount ? new Intl.NumberFormat("es-CL", { style: "currency", currency: tender.currency || "CLP", maximumFractionDigits: 0 }).format(tender.estimated_amount) : "N/A"}

Closing Date

{tender.closing_date || "TBD"}

Region

{tender.region || "Nacional"}

Sector

{tender.sector || "General"}

)} {/* Buyer Risk & Experience Cards */} {(tender?.buyer_complaints !== undefined || tender?.buyer_purchases !== undefined || tenderDetails?.metadata?.buyer_complaints !== undefined) && (
10 ? 'bg-red-500/10 border-red-500/30 shadow-lg shadow-red-500/10' : 'bg-white/5 border-white/5'}`}>

Complaints (Last 12m)

10 ? 'text-red-400' : 'text-white'}`}> {tender?.buyer_complaints ?? tenderDetails?.metadata?.buyer_complaints ?? 0}

{(tender?.buyer_complaints ?? tenderDetails?.metadata?.buyer_complaints ?? 0) > 10 ? '⚠️' : '✅'}

Purchases Executed

{tender?.buyer_purchases ?? tenderDetails?.metadata?.buyer_purchases ?? "1.6k+"}

🛒
)} {/* Guarantees Section */} {tenderDetails?.metadata?.guarantees && tenderDetails.metadata.guarantees.length > 0 && (
{tenderDetails.metadata.guarantees.map((g: any, i: number) => (

{g.type}

{g.amount}

🛡️
))}
)} {tender?.description && (

Detailed Scope

{tender.description}

)} {tender?.items && tender.items.length > 0 && (
{tender.items.slice(0, 3).map((item, idx) => ( ))} {tender.items.length > 3 && ( )}
Item Name Qty
{item.name} {item.quantity} {item.unit}
+ {tender.items.length - 3} more items...
)} {/* Detailed Scraped Items */} {tenderDetails?.metadata?.detailed_items && tenderDetails.metadata.detailed_items.length > 0 && (

Portal Line Items Intelligence

{tenderDetails.metadata.detailed_items.map((item: any, idx: number) => (
{item.code}

"{item.description}"

))}
)} {/* Scraped Intelligence / Tabs */} {tenderDetails && (
{tenderDetails.tabs?.history?.found && (
📜 History Available
)} Visit Official Site 🔗 {tenderDetails.metadata?.question_count && tenderDetails.metadata.question_count > 0 ? ( View {tenderDetails.metadata.question_count} Questions in Portal 🔗 ) : ( tenderDetails.tabs?.questions?.found && (
Q&A Active
) )} {tenderDetails.tabs?.opening?.found && (
🔓 Opening Log Found
)} {tenderDetails.metadata?.has_adjudication && (
🏆 Adjudicated
)}
)} {/* Scraped Attachments (Extended List) */} {tenderDetails?.attachments && tenderDetails.attachments.length > 0 && (

Scraped Attachments ({tenderDetails.attachments.length})

{isLoadingDetails && Refreshing...}
{tenderDetails.attachments.slice(0, 6).map((att, idx) => ( {att.name.toLowerCase().includes('bases') ? '⚖️' : att.name.toLowerCase().includes('tecnico') ? '🛠️' : att.name.toLowerCase().includes('anexo') ? '📝' : '📄'}

{att.name}

Direct Download 📥

))} {tenderDetails.attachments.length > 6 && (
+ {tenderDetails.attachments.length - 6} more attachments
)}
)}

Document Corral

{/* The Corral (Animal Pen) */}
{corral.map((item) => { const icon = getFileIcon(item.file.name); return (
); })}
{corral.length === 0 ? "No documents in the corral." : `${corral.length} document(s) ready.`}
{isUploading &&

✨ Bringing animal to corral...

} {/* PDF Viewer for Active Selection */} {activeAnimalId && pdfUrls[activeAnimalId] && (
{/* Fallback Overlay for blocked frames */}

Document Preview Mode

📄

{corral.find(a => a.id === activeAnimalId)?.file.name}

OPEN FULL PDF ↗
)}
{/* Agents Row (Visual feedback & Configuration) */}
{agents.map((agent) => (
{agent.avatar}
{agent.role}
{agent.name}
{agentModels[agent.id as keyof typeof agentModels]}
{/* Model Selector Popover */} {activeSettings === agent.id && (

Select Engine

{[ "Gemini 2.5 Flash", "DeepSeek-V3 (Featherless)", "Qwen-2.5 (Featherless)", "Llama-3.3-70B (Groq)", "Llama-3.1-8B (Groq)", "Mixtral-8x7B (Groq)", "Gemma-4-31B (Featherless)", "Llama-3.1-8B (Featherless)" ].map(model => ( ))}
)}
))}
{/* Running State Log */} {isRunning && (

Pipeline in Progress

{statusLog.map((log, i) => (
[{new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })}]

{log}

))}
)} {/* Error State */} {error && (
⚠️

Analysis Failed

{error}

)} {/* Analysis Results & Intelligent Sections */} {activeAnalysis && (
{/* Main Analysis Card */}
{/* Professional Print Header */}

ANDESOPS AI

Intelligent Bidding Analysis Report

Agent Consensus

{activeAnalysis.fit_score}% Fit Score

Analyzing: {corral.find(a => a.id === activeAnimalId)?.file.name || tender?.name}
{activeAnalysis.decision}
Visit Official Site 🔗

{activeAnalysis.executive_summary}

{/* Requirement Q&A Section */} {activeAnalysis.requirement_responses && activeAnalysis.requirement_responses.length > 0 && (
📋

{activeAnalysis.requirement_responses.length > 3 ? `Actual Market Questions (${activeAnalysis.requirement_responses.length})` : "Intelligence Requirement Response"}

{activeAnalysis.requirement_responses.length > 3 && (
Synced with Portal
)}
{activeAnalysis.requirement_responses.map((item, i) => (
Q.

{item.question}

A.

{item.answer}

))}
)}

⚠️ Compliance Gaps

    {activeAnalysis.compliance_gaps.map((gap, i) => (
  • {gap}
  • ))}

💎 Tech Requirements

    {activeAnalysis.key_requirements.map((req, i) => (
  • {req}
  • ))}
{/* Audit Log / Agent Thoughts Sticky Column */}

Agent Intel Log

{activeAnalysis.audit_log?.map((log, i) => (
🤖
{i < (activeAnalysis.audit_log?.length ?? 0) - 1 &&
}

{log}

))}
)} {/* Compliance Anexos Section (Moved to prevent overlap with Chat) */} {generatedAnnexes.length > 0 && (
📄

Compliance: Anexos Express

Official annexes pre-filled with company data.

{generatedAnnexes.map((annex, i) => (
Template
{annex.name}
{annex.content}
))}
)} {/* Expert Consultation Chat (Bottom Section) */} {tender && (
💬

Expert Agent Consultation

Deep-dive into specific questions with our AI agents.

)}
); }