'use client' import { useState, useEffect, useRef } from 'react' import React from 'react' import { CompanyProfile } from '@/lib/sdk/types' import { useSDK } from '@/lib/sdk' import { isMachineBuilderIndustry, getWizardSteps } from '../_components/constants' const INITIAL_FORM_DATA: Partial = { companyName: '', legalForm: undefined, industry: [], industryOther: '', foundedYear: null, businessModel: undefined, offerings: [], offeringUrls: {}, companySize: undefined, employeeCount: '', annualRevenue: '', headquartersCountry: 'DE', headquartersCountryOther: '', headquartersStreet: '', headquartersZip: '', headquartersCity: '', headquartersState: '', hasInternationalLocations: false, internationalCountries: [], targetMarkets: [], primaryJurisdiction: 'DE', isDataController: true, isDataProcessor: false, dpoName: null, dpoEmail: null, legalContactName: null, legalContactEmail: null, isComplete: false, completedAt: null, } function buildProfilePayload(formData: Partial, projectId: string | null, isComplete: boolean) { return { project_id: projectId || null, company_name: formData.companyName || '', legal_form: formData.legalForm || 'GmbH', industry: formData.industry || [], industry_other: formData.industryOther || '', founded_year: formData.foundedYear || null, business_model: formData.businessModel || 'B2B', offerings: formData.offerings || [], offering_urls: formData.offeringUrls || {}, company_size: formData.companySize || 'small', employee_count: formData.employeeCount || '', annual_revenue: formData.annualRevenue || '', headquarters_country: formData.headquartersCountry || 'DE', headquarters_country_other: formData.headquartersCountryOther || '', headquarters_street: formData.headquartersStreet || '', headquarters_zip: formData.headquartersZip || '', headquarters_city: formData.headquartersCity || '', headquarters_state: formData.headquartersState || '', has_international_locations: formData.hasInternationalLocations || false, international_countries: formData.internationalCountries || [], target_markets: formData.targetMarkets || [], primary_jurisdiction: formData.primaryJurisdiction || 'DE', is_data_controller: formData.isDataController ?? true, is_data_processor: formData.isDataProcessor ?? false, dpo_name: formData.dpoName || '', dpo_email: formData.dpoEmail || '', is_complete: isComplete, processing_systems: (formData as any).processingSystems || [], ai_systems: (formData as any).aiSystems || [], technical_contacts: (formData as any).technicalContacts || [], existing_certifications: (formData as any).existingCertifications || [], target_certifications: (formData as any).targetCertifications || [], target_certification_other: (formData as any).targetCertificationOther || '', review_cycle_months: (formData as any).reviewCycleMonths || 12, repos: (formData as any).repos || [], document_sources: (formData as any).documentSources || [], ...(formData.machineBuilder ? { machine_builder: { product_types: formData.machineBuilder.productTypes || [], product_description: formData.machineBuilder.productDescription || '', product_pride: formData.machineBuilder.productPride || '', contains_software: formData.machineBuilder.containsSoftware || false, contains_firmware: formData.machineBuilder.containsFirmware || false, contains_ai: formData.machineBuilder.containsAI || false, ai_integration_type: formData.machineBuilder.aiIntegrationType || [], has_safety_function: formData.machineBuilder.hasSafetyFunction || false, safety_function_description: formData.machineBuilder.safetyFunctionDescription || '', autonomous_behavior: formData.machineBuilder.autonomousBehavior || false, human_oversight_level: formData.machineBuilder.humanOversightLevel || 'full', is_networked: formData.machineBuilder.isNetworked || false, has_remote_access: formData.machineBuilder.hasRemoteAccess || false, has_ota_updates: formData.machineBuilder.hasOTAUpdates || false, update_mechanism: formData.machineBuilder.updateMechanism || '', export_markets: formData.machineBuilder.exportMarkets || [], critical_sector_clients: formData.machineBuilder.criticalSectorClients || false, critical_sectors: formData.machineBuilder.criticalSectors || [], oem_clients: formData.machineBuilder.oemClients || false, ce_marking_required: formData.machineBuilder.ceMarkingRequired || false, existing_ce_process: formData.machineBuilder.existingCEProcess || false, has_risk_assessment: formData.machineBuilder.hasRiskAssessment || false, }, } : {}), } } export function useCompanyProfileForm() { const { state, dispatch, setCompanyProfile, goToNextStep, projectId } = useSDK() const [currentStep, setCurrentStep] = useState(1) const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const [formData, setFormData] = useState>(INITIAL_FORM_DATA) const [draftSaveStatus, setDraftSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle') const showMachineBuilderStep = isMachineBuilderIndustry(formData.industry || []) const wizardSteps = getWizardSteps(formData.industry || []) const lastStep = wizardSteps[wizardSteps.length - 1].id const profileApiUrl = (extra?: string) => { const params = new URLSearchParams() if (projectId) params.set('project_id', projectId) const qs = params.toString() const base = '/api/sdk/v1/company-profile' + (extra || '') return qs ? `${base}?${qs}` : base } // Load existing profile useEffect(() => { let cancelled = false async function loadFromBackend() { try { const apiUrl = '/api/sdk/v1/company-profile' + (projectId ? `?project_id=${encodeURIComponent(projectId)}` : '') const response = await fetch(apiUrl) if (response.ok) { const data = await response.json() if (data && !cancelled) { const backendProfile: Partial = { companyName: data.company_name || '', legalForm: data.legal_form || undefined, industry: Array.isArray(data.industry) ? data.industry : (data.industry ? [data.industry] : []), industryOther: data.industry_other || '', foundedYear: data.founded_year || undefined, businessModel: data.business_model || undefined, offerings: data.offerings || [], offeringUrls: data.offering_urls || {}, companySize: data.company_size || undefined, employeeCount: data.employee_count || '', annualRevenue: data.annual_revenue || '', headquartersCountry: data.headquarters_country || 'DE', headquartersCountryOther: data.headquarters_country_other || '', headquartersStreet: data.headquarters_street || '', headquartersZip: data.headquarters_zip || '', headquartersCity: data.headquarters_city || '', headquartersState: data.headquarters_state || '', hasInternationalLocations: data.has_international_locations || false, internationalCountries: data.international_countries || [], targetMarkets: data.target_markets || [], primaryJurisdiction: data.primary_jurisdiction || 'DE', isDataController: data.is_data_controller ?? true, isDataProcessor: data.is_data_processor ?? false, dpoName: data.dpo_name || '', dpoEmail: data.dpo_email || '', isComplete: data.is_complete || false, processingSystems: data.processing_systems || [], aiSystems: data.ai_systems || [], technicalContacts: data.technical_contacts || [], existingCertifications: data.existing_certifications || [], targetCertifications: data.target_certifications || [], targetCertificationOther: data.target_certification_other || '', reviewCycleMonths: data.review_cycle_months || 12, repos: data.repos || [], documentSources: data.document_sources || [], } as any setFormData(backendProfile) setCompanyProfile(backendProfile as CompanyProfile) if (backendProfile.isComplete) { setCurrentStep(99) dispatch({ type: 'COMPLETE_STEP', payload: 'company-profile' }) } return } } } catch { /* Backend not available, fall through to SDK state */ } if (!cancelled && state.companyProfile) { setFormData(state.companyProfile) if (state.companyProfile.isComplete) setCurrentStep(99) } } loadFromBackend() return () => { cancelled = true } // eslint-disable-next-line react-hooks/exhaustive-deps }, [projectId]) const updateFormData = (updates: Partial) => { setFormData(prev => ({ ...prev, ...updates })) } // Auto-save to SDK context (debounced) const autoSaveRef = useRef | null>(null) const initialLoadDone = useRef(false) useEffect(() => { if (!initialLoadDone.current) { if (formData.companyName !== undefined) initialLoadDone.current = true return } if (currentStep === 99) return if (autoSaveRef.current) clearTimeout(autoSaveRef.current) autoSaveRef.current = setTimeout(() => { const hasData = formData.companyName || (formData.industry && formData.industry.length > 0) if (hasData) setCompanyProfile({ ...formData, isComplete: false, completedAt: null } as CompanyProfile) }, 500) return () => { if (autoSaveRef.current) clearTimeout(autoSaveRef.current) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [formData, currentStep]) // Auto-save draft to backend (debounced, 2s) const backendAutoSaveRef = useRef | null>(null) const draftSaveTimerRef = React.useRef | null>(null) useEffect(() => { if (!initialLoadDone.current) return if (currentStep === 99) return const hasData = formData.companyName || (formData.industry && formData.industry.length > 0) if (!hasData) return if (backendAutoSaveRef.current) clearTimeout(backendAutoSaveRef.current) backendAutoSaveRef.current = setTimeout(async () => { try { await fetch(profileApiUrl(), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(buildProfilePayload(formData, projectId, false)), }) setDraftSaveStatus('saved') if (draftSaveTimerRef.current) clearTimeout(draftSaveTimerRef.current) draftSaveTimerRef.current = setTimeout(() => setDraftSaveStatus('idle'), 3000) } catch { /* Silent fail for auto-save */ } }, 2000) return () => { if (backendAutoSaveRef.current) clearTimeout(backendAutoSaveRef.current) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [formData, currentStep]) const saveProfileDraft = async () => { setDraftSaveStatus('saving') try { await fetch(profileApiUrl(), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(buildProfilePayload(formData, projectId, false)), }) setCompanyProfile({ ...formData, isComplete: false, completedAt: null } as CompanyProfile) setDraftSaveStatus('saved') if (draftSaveTimerRef.current) clearTimeout(draftSaveTimerRef.current) draftSaveTimerRef.current = setTimeout(() => setDraftSaveStatus('idle'), 3000) } catch (err) { console.error('Draft save failed:', err) setDraftSaveStatus('error') if (draftSaveTimerRef.current) clearTimeout(draftSaveTimerRef.current) draftSaveTimerRef.current = setTimeout(() => setDraftSaveStatus('idle'), 5000) } } const completeAndSaveProfile = async () => { if (autoSaveRef.current) clearTimeout(autoSaveRef.current) if (backendAutoSaveRef.current) clearTimeout(backendAutoSaveRef.current) const completeProfile: CompanyProfile = { ...formData, isComplete: true, completedAt: new Date() } as CompanyProfile try { await fetch(profileApiUrl(), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(buildProfilePayload(formData, projectId, true)), }) } catch (err) { console.error('Failed to save company profile to backend:', err) } setCompanyProfile(completeProfile) dispatch({ type: 'COMPLETE_STEP', payload: 'company-profile' }) dispatch({ type: 'SET_STATE', payload: { projectVersion: (state.projectVersion || 0) + 1 } }) setCurrentStep(99) } const handleNext = () => { if (currentStep < lastStep) { const nextStep = currentStep + 1 if (nextStep === 7 && !showMachineBuilderStep) { completeAndSaveProfile() return } saveProfileDraft() setCurrentStep(nextStep) } else { completeAndSaveProfile() } } const handleBack = () => { if (currentStep > 1) { saveProfileDraft(); setCurrentStep(prev => prev - 1) } } const handleDeleteProfile = async () => { setIsDeleting(true) try { const response = await fetch(profileApiUrl(), { method: 'DELETE' }) if (response.ok) { setFormData(INITIAL_FORM_DATA) setCurrentStep(1) dispatch({ type: 'SET_STATE', payload: { companyProfile: undefined } }) } } catch (err) { console.error('Failed to delete company profile:', err) } finally { setIsDeleting(false); setShowDeleteConfirm(false) } } const canProceed = () => { switch (currentStep) { case 1: return formData.companyName && formData.legalForm case 2: return formData.businessModel && (formData.offerings?.length || 0) > 0 case 3: return formData.companySize case 4: return formData.headquartersCountry && (formData.targetMarkets?.length || 0) > 0 case 5: return true case 6: return true case 7: return (formData.machineBuilder?.productDescription?.length || 0) > 0 default: return false } } const isLastStep = currentStep === lastStep || (currentStep === 6 && !showMachineBuilderStep) return { formData, updateFormData, currentStep, setCurrentStep, wizardSteps, showMachineBuilderStep, isLastStep, draftSaveStatus, canProceed, handleNext, handleBack, handleDeleteProfile, showDeleteConfirm, setShowDeleteConfirm, isDeleting, goToNextStep, } }