import type { Preparation, PreparationDocument, } from '@/components/private-investment-preparation'; function csrfToken(): string { try { return decodeURIComponent( document.cookie .split(';') .map((value) => value.trim()) .find((c) => c.startsWith('XSRF-TOKEN=')) ?.slice('XSRF-TOKEN='.length) ?? '', ); } catch { throw new Error( 'A sessão não pôde ser validada. Actualize a página e tente novamente.', ); } } export type ContextDocument = { resource_id: number; document_id: string; metadata_version: number; document_version: number; edition: string | null; sha256: string; source_reference: string; title: string; support_id: string; support_version: number; section: string | null; opportunity_id: number; download_url: string; }; export type ContextReference = { key: string; kind: string; id: string; title: string; href: string; version: number | string | null; sha256: string; fields: Record; analysis?: { documented_sector?: string | null; documented_location?: string | null; [key: string]: unknown; }; documents?: ContextDocument[]; visibility?: 'private' | 'public'; state?: string | null; }; export type RequirementsAssessment = { version: string; fiscal_notice: string; requirements: { opportunity_key: string; opportunity_title: string; code: string; title: string; state: string; step: string; authority: string; documents: string[]; basis: string; source_url: string; decision: string; }[]; }; export type OpportunityChoice = { park?: ContextReference | null; portfolio_origins?: { id: string; title: string; kind: string; href: string; version: number; sha256: string; }[]; corridor_key?: string | null; corridor_name?: string | null; allowed_provinces?: import('@/components/investment-territory').TerritoryOption[]; activities?: string[]; territory_path?: import('@/components/investment-territory').TerritoryOption[]; confirm_location_change?: boolean; opportunity: ContextReference; territory: ContextReference | null; location_reason: string; location_state: string; coverage?: string | null; source?: string | null; source_sha256?: string | null; }; export type DossierContext = { complements?: import('@/components/investment-complements').Complement[]; requirements_assessment?: RequirementsAssessment; opportunity_choices?: OpportunityChoice[]; revision: number; selection: Partial< Record< 'portfolio' | 'opportunity' | 'sector' | 'territory' | 'park', ContextReference > >; fields: Record; provenance: Record< string, { kind?: string; title?: string; id?: string; version?: number | string; } >; updated_at?: string; dependency_hash?: string; }; export type ContextView = { private_investment?: Preparation | null; preparation_documents?: PreparationDocument[]; simulator_context_revision?: number | null; case: { id: string; title: string; version: number }; context: DossierContext; can_write: boolean; next: { code: string; title: string } | null; steps: { code: string; title: string; state: string }[]; results: { id: string; engine: string; context_state?: string }[]; requests: { id: string; status: string; protocol: string | null; context_state?: string; }[]; prefill: Record>; notice: string; }; export type ContextPlan = { complements?: import('@/components/investment-complements').Complement[]; requirements_assessment?: RequirementsAssessment; opportunity_choices?: OpportunityChoice[]; private_investment?: { before: Preparation; after: Preparation } | null; preview_hash: string; version: number; revision: number; fields: Record; selection: DossierContext['selection']; updated: { field: string; before: string | null; after: string }[]; maintained: { field: string; value: string; proposed: string }[]; dependent_results: { id: string; engine: string }[]; requests: { id: string; status: string; protocol: string | null; version: number; }[]; review_steps: string[]; documents_preserved: number; notice: string; }; // Leave headroom below the browser's shared 64 KiB keepalive budget. const contextKeepaliveBudget = 60000; let contextKeepaliveBytes = 0; export async function contextApi( url: string, body?: unknown, method = 'POST', keepalive = false, ): Promise { const serializedBody = body === undefined ? undefined : JSON.stringify(body); const bytes = serializedBody ? new TextEncoder().encode(serializedBody).byteLength : 0; const useKeepalive = keepalive && contextKeepaliveBytes + bytes < contextKeepaliveBudget; if (useKeepalive) contextKeepaliveBytes += bytes; try { const response = await fetch(url, { keepalive: useKeepalive, method: body === undefined ? 'GET' : method, credentials: 'same-origin', headers: { Accept: 'application/json', ...(body === undefined ? {} : { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': csrfToken(), }), }, ...(serializedBody === undefined ? {} : { body: serializedBody }), }); const data = await response.json().catch(() => null); if (!response.ok || data === null) throw new Error( data?.message || `Não foi possível recuperar ou guardar o contexto (HTTP ${response.status}).`, ); return data as T; } finally { if (useKeepalive) contextKeepaliveBytes -= bytes; } } export const contextFieldNames: Record = { investment_form: 'Tipo do investimento', investment_preparation: 'Preparação do pedido', investment_activity_scope: 'Natureza da actividade', implementation_mode: 'Modalidade da empresa', capital_origin: 'Origem dos capitais', site_tenure: 'Utilização das instalações', project_concession: 'PPP ou concessão empresarial', significant_implications: 'Implicações significativas', land_extension: 'Extensão de terra', forest_concession: 'Concessão florestal', forest_concession_hectares: 'Área florestal em hectares', total_investment: 'Valor total do investimento', area_required: 'Área de terra requerida', area_unit: 'Unidade da área', title: 'Título do investimento', territory_id: 'Localização documentada', location_source: 'Origem da localização', description: 'Descrição', project_title: 'Título do investimento', project_summary: 'Descrição do investimento', sector_id: 'Sector (identificador)', sector: 'Sector', province_id: 'Província (identificador)', district_id: 'Distrito (identificador)', admin_post_id: 'Posto administrativo', locality_id: 'Localidade', province: 'Província', district: 'Distrito', location: 'Localização', currency: 'Moeda do projecto', person_type: 'Tipo de pessoa', fiscal_period: 'Ano fiscal', fiscal_periodicity: 'Periodicidade', fiscal_month: 'Mês fiscal', fiscal_residency_country: 'País de residência fiscal declarado', activity_id: 'Actividade', project_type: 'Modalidade do investimento', maturity_stage: 'Fase do investimento', expected_start_date: 'Início previsto', investor_origin: 'Origem do investidor', entity_stage: 'Situação da entidade', tax_residency_country: 'Residência fiscal declarada', investment_regime: 'Enquadramento territorial consultado', special_zone_id: 'ZEE, ZFI ou parque', };