"""Prepare review-only Apache/environment plan and the existing UUID backlog inventory."""
from pathlib import Path
import json,hashlib,re
ROOT=Path(__file__).resolve().parent.parent
OUT=ROOT/'outputs/investmoz-country02-https-20261011';OUT.mkdir(exist_ok=True)
sha=lambda p:hashlib.sha256(Path(p).read_bytes()).hexdigest()
read=lambda p:json.loads(Path(p).read_text(encoding='utf-8-sig'))
old=read(ROOT/'outputs/investmoz-country02-c110-20261011/manifesto-candidata.json')
meta=read(ROOT/'storage/app/private/investmoz-v2/portal-audit/country-https-latest.json')
vhost=Path('C:/laragon/etc/apache2/sites-enabled/auto.portal-investidor.test.conf')
apache=Path('C:/laragon/bin/apache/httpd-2.4.54-win64-VS16/conf/httpd.conf')
ssl=Path('C:/laragon/etc/apache2/httpd-ssl.conf')
baseline={'active_vhost':{'path':str(vhost),'sha256':sha(vhost)},'apache_config':{'path':str(apache),'sha256':sha(apache)},'ssl_include':{'path':str(ssl),'sha256':sha(ssl)},'active_manifest_sha256':sha(ROOT/'public/build/manifest.json')}
(OUT/'configuracao-ativa-leitura.json').write_text(json.dumps(baseline,indent=2),encoding='utf-8')
# Proposed file deliberately stays outside sites-enabled. No keys, live identities or .env copied.
proposal='''# PROPOSTA PARA REVISÃO. NÃO INSTALADA. Substitui o vhost HTTP correspondente.
# Conferir Listen 443 único e módulos ssl/rewrite/headers antes de incluir.
ServerName portal-investidor.test
RewriteEngine On
RewriteCond %{REQUEST_METHOD} !^(GET|HEAD)$
RewriteRule ^ - [R=405,L]
RewriteRule ^ https://portal-investidor.test%{REQUEST_URI} [R=308,L,NE]
ServerName portal-investidor.test
DocumentRoot "C:/laragon/www/portal-investidor/public"
SSLEngine On
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCertificateFile "C:/Users/macbook/.config/investmoz/tls/portal-investidor.test-fullchain.pem"
SSLCertificateKeyFile "C:/Users/macbook/.config/investmoz/tls/portal-investidor.test-key.pem"
AllowOverride All
Require all granted
Header always set X-Content-Type-Options "nosniff"
# HSTS diferido para não impedir a reversão do ensaio inicial.
'''
(OUT/'portal-investidor.test-https.proposta.conf').write_text(proposal,encoding='utf-8')
(OUT/'ambiente-https.proposta.txt').write_text('''# Apenas estes valores públicos propostos. Preservar DB, APP_KEY e restantes flags.
APP_URL=https://portal-investidor.test
SESSION_COOKIE=portal_investidor_https
SESSION_SECURE_COOKIE=true
SESSION_DOMAIN=null
SESSION_PATH=/
SESSION_SAME_SITE=lax
# Só habilitar depois de 050000, auditoria e prova funcional correspondentes:
PORTAL_ORGANIZATION_COUNTRY_ENABLED=false
''',encoding='utf-8')
groups={
'components/simulation-form.tsx':(['GET /simulador-fiscal','GET /servicos'], 'Público ou investidor', 'Autoguardar rascunho e calcular cenário', 'P1: consumidor público atual; chamada diferida no autoguardar/calcular, não no primeiro render', 'Atual; simulação/core. Falha HTTP inferida do tipo de API observado e dos call sites, não repetida como percurso completo'),
'components/desk-internal-operations.tsx':(['GET /balcao-virtual/pedidos/{id}'], 'Equipa interna autorizada', 'Abrir comunicações; registar e preparar seguinte', 'P1 se Balcão instalado: falha no render', 'Balcão não incluído na candidata; conferir instalação/autorização para alcançar o ramo'),
'pages/desk/panel.tsx':(['GET /balcao-virtual/painel'], 'Investidor ou equipa autorizada', 'Abrir painel e preparar dossier', 'P1 se Balcão instalado: falha no render', 'Não ativar Balcão por arrasto'),
'pages/desk/case.tsx':(['GET /balcao-virtual/dossiers/{id}'], 'Titular ou equipa autorizada', 'Abrir dossier e preparar pedido', 'P1 se Balcão instalado: falha no render', 'Não ativar Balcão por arrasto'),
'pages/desk/request.tsx':(['GET /balcao-virtual/pedidos/{id}'], 'Titular ou equipa autorizada', 'Abrir pedido; enviar mensagem e preparar seguinte', 'P1 se Balcão instalado: falha no render', 'Não ativar Balcão por arrasto'),
'pages/investor-path/index.tsx':(['GET /investimentos'], 'Investidor autenticado', 'Abrir/criar dossier', 'P1 antes de disponibilizar percurso em HTTP: falha no render', '190000/flag ainda ausentes no alvo; provas A/B loopback preservadas'),
'pages/investor-path/show.tsx':(['GET /investimentos/{id}'], 'Titular ou equipa com acesso', 'Abrir percurso e preparar apoio', 'P1 antes de disponibilizar percurso em HTTP: falha no render', '190000/flag ausentes; não ativar'),
'pages/management/opportunity-draft.tsx':(['GET /management/opportunities/create','GET /management/opportunities/{id}/edit','GET /minhas-oportunidades/create','GET /minhas-oportunidades/{id}/edit'], 'Gestor autorizado ou Promotor representado', 'Abrir/criar/editar rascunho', 'P1 antes de habilitar gestão/perfis em HTTP: falha no render', '030000/040000/flags ausentes no alvo; não ativar'),
'pages/management/opportunity-resources.tsx':(['GET /management/opportunities/{id}/resources','GET /minhas-oportunidades/{id}/resources'], 'Gestor ou Promotor autorizado ao registo', 'Abrir recursos, anexar, guardar geometria e repetir', 'P1 antes de habilitar recursos em HTTP: falha no render', '030000/040000 ausentes; não ativar'),
'pages/management/territory-geometry-import.tsx':(['GET /management/territory-geometry-import'], 'Gestor com MFA', 'Prévia/importar/reverter/trocar filtros', 'P1 antes de importar cartografia em HTTP: falha no render', 'Importação fora de COUNTRY02; não aplicar'),
'pages/partnerships/editor.tsx':(['GET /parcerias/criar','GET /parcerias/{id}/editar'], 'Autor/representante autorizado', 'Abrir e guardar oferta/pedido de parceria', 'P1 se Parcerias instalado: falha no render', 'Parcerias fora da candidata; sem ensaio funcional novo'),
'pages/partnerships/room.tsx':(['GET /parcerias/salas/{id}'], 'Participante autorizado da sala', 'Abrir/enviar mensagem e preparar seguinte', 'P1 se Parcerias instalado: falha no render', 'Parcerias fora da candidata; sem ensaio funcional novo'),
'pages/profiles/installation.tsx':(['GET /management/profile-install','POST /management/profile-install','POST /management/profile-install/bootstrap'], 'Gestor habilitado/MFA', 'Confirmar instalação e bootstrap', 'P1: instalador acessível antes da instalação; falha ao confirmar e chave nova a cada tentativa', '040000 não instalado; somente preparação fora da candidata'),
'pages/profiles/request.tsx':(['GET /perfil/promotor','POST /perfil/promotor'], 'Utilizador autenticado', 'Abrir/pedir perfil e preparar novo pedido', 'P1 antes de habilitar perfis em HTTP: falha no render', '040000/flag ausentes; não ativar'),
}
entries=[]
for item in read(ROOT/'outputs/investmoz-country02-c110-20261011/inventario-uuid.json')['other_occurrences']:
relative=item['file'].removeprefix('resources/js/');routes,profile,action,priority,availability=groups[relative]
text=(ROOT/item['file']).read_text(encoding='utf-8');lines=text.splitlines();assert 'crypto.randomUUID()' in lines[item['line']-1]
entries.append({**item,'routes':routes,'profile':profile,'action':action,'priority':priority,'availability':availability,'evidence':'C110 HTTP hostname: typeof crypto.randomUUID=undefined; callsite in existing source. No invented activation or full-route test.','required_fix':'Server operation UUID stable across retries, preserving existing authorization/idempotency; or approved compatible cryptographic mechanism. No Math.random/timestamp.','qa_https_effect':'API available after genuinely trusted HTTPS; per-consumer idempotency/recovery still requires tests','status':'backlog; not included in COUNTRY02'})
assert len(entries)==25
inventory={'count':25,'country02_calls_remaining':0,'entries':entries,'core_priority':['Simulador fiscal/autoguardar/calcular','040000 installer attempt stability'],'acceptance_not_extended':['A/B','C37'],'active_changes':0}
(OUT/'uuid-backlog.json').write_text(json.dumps(inventory,ensure_ascii=False,indent=2),encoding='utf-8')
lines=['# Chamadas UUID restantes — backlog existente','', '25 ocorrências em 14 componentes. COUNTRY02 preparado tem zero. A capacidade HTTP foi demonstrada no hostname equivalente; as falhas por consumidor abaixo são inferências dos call sites e dependem de instalação/autorização. HTTPS disponibiliza a API, mas não comprova estabilidade da chave ou recuperação de cada operação.','', '|Componente|Ocorrências|Rotas e perfil|Ação afetada/prioridade|','|---|---:|---|---|']
for file,g in groups.items():
count=sum(e['file']=='resources/js/'+file for e in entries)
lines.append(f"|`{file}`|{count}|{'
'.join(g[0])}; {g[1]}|{g[2]}. {g[3]}|")
lines+=['','Prioridade imediata no ambiente HTTP atual: simulador público no autoguardar/calcular e instalador040000 ao confirmar. Os ramos guardados por instalações/flags ausentes devem ser corrigidos antes de sua disponibilização, sem aplicar esses pacotes como dependência de COUNTRY02. Preservar as aceites A/B e C37 nos seus âmbitos.','']
(OUT/'uuid-backlog.md').write_text('\n'.join(lines),encoding='utf-8')
plan={'active_origin':'https://portal-investidor.test','existing_http_origin':'http://portal-investidor.test','qa_origin':meta['base'],'code_hash':old['code_hash'],'production_manifest_sha256':old['production']['manifest_sha256'],'baseline':baseline,
'certificate_required':{'SAN':['portal-investidor.test'],'EKU':'serverAuth','trusted_chain':'CA local/organizacional aceite nos browsers que usam este hostname local; NÃO reutilizar a CA QA nem o seu certificado','private_key_location':'C:/Users/macbook/.config/investmoz/tls/portal-investidor.test-key.pem','fullchain_location':'C:/Users/macbook/.config/investmoz/tls/portal-investidor.test-fullchain.pem','issuer_and_expiry':'To be fixed with actual selected active certificate before authorization/application'},
'application_values':{'APP_URL':'https://portal-investidor.test','SESSION_COOKIE':'portal_investidor_https','SESSION_SECURE_COOKIE':True,'SESSION_DOMAIN':None,'SESSION_PATH':'/','SESSION_SAME_SITE':'lax'},
'urls':{'preview':'https://portal-investidor.test/management/opportunity-resources-install/organization-country','profile':'https://portal-investidor.test/investidor/profile','logout':'POST https://portal-investidor.test/logout'},
'steps':['Verify exact final package, certificate SAN/chain/validity/private key permissions, all active baseline hashes and backup/restored clone <=24h','Stage C110 application sources and reviewed production build with COUNTRY02 flag false; preserve previous sources and all immutable assets','Only after explicit HTTPS authorization: validate Apache TLS proposal httpd -t, configure exact host and selected active certificate, effective .env/cache values, graceful reload; redirect GET/HEAD HTTP to HTTPS and reject HTTP credentials','Use actual manager, verified email, normal MFA on HTTPS; check effective target5432 investemoz, code54 and preview hash, ONLY050000/two nullable columns/0tables/users/representations','POST same server key/hash/body for retries; check one audit and one migration; no030/040/190','Enable only COUNTRY02 and test legitimate organization/membership permissions, save/update/recover, encrypted Back/Forward and logout/new session'],
'stop_conditions':['certificate trust/SAN/expiry/baseline/package/backup mismatch','actual preview differs','private history/session/authorization/asset/mixed-content failure','missing explicit authorization for HTTPS and replacement delivery'],
'reversal':['Disable COUNTRY02 first; keep050000, country values/provenance, ledger/audit and backups; never drop columns','Restore preserved previous source/build manifest atomically without deleting immutable assets','Revert only the specific Apache/env/cache changes from snapshots and validate/reload; change session cookie namespace to avoid reuse','Preferred safe containment keeps HTTPS and flag disabled; a full HTTP reversion removes the HTTPS deliverable and reopens C111 for private pages; do not claim working private COUNTRY02 over HTTP','No key:generate, MFA disable, table/account/representation changes, HSTS during initial rollout or other packages'],
'active_configuration_changed':False,'authorization_required':['replacement final delivery','HTTPS certificate/trust distribution + exact Apache/env change','050000 authenticated action + COUNTRY02 flag only']}
(OUT/'plano-https-ativo.json').write_text(json.dumps(plan,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({'output':str(OUT),'UUID_backlog_occurrences':25,'active_config_changed':False}))