Files
Investidor-V2/app/Services/SectorResourceReferences.php
2026-11-11 14:13:51 +02:00

83 lines
6.7 KiB
PHP

<?php
namespace App\Services;
use App\Support\CheckedFile;
use Illuminate\Support\Facades\Validator;
/** Versioned reference content over existing sector pages and private dossiers. */
final class SectorResourceReferences
{
public const SOURCES = [
'mineracao' => '7202fb5986b40e62347867c653770030683ffec2701a2921e84342f15e9f3d8a',
'energia-especifica' => '2d9d94ac70115ade43c4093a4d5b302aaa93623a149f936615c38ba67b1e2bd2',
];
// Generated data are checked independently from the unchanged Word originals.
public const DATA = [
'mineracao' => ['file' => 'mining-reference-20261111.json', 'hash' => '9274f4059ef5d7aa8ec625f1c6f8fd4a11c8e314c5282c5f24c38dbcf7c68bb5'],
'energia-especifica' => ['file' => 'energy-reference-20261111.json', 'hash' => '3c0fe65a0682d952987f63bb48aaaada9ba4f8d10ae40149658211f56fa8e7ac'],
];
/** @return array<string,mixed> */
public function data(string $sector): array
{
abort_unless(isset(self::DATA[$sector]), 404);
$path = database_path('data/'.self::DATA[$sector]['file']);
abort_unless(hash_equals(self::DATA[$sector]['hash'], CheckedFile::sha256($path)), 503, 'A versão das referências sectoriais mudou.');
$data = json_decode(CheckedFile::read($path), true, 512, JSON_THROW_ON_ERROR);
abort_unless(($data['source_sha256'] ?? null) === self::SOURCES[$sector], 503, 'Reconciliar a versão da referência antes de usar.');
return $data;
}
/** @return array<string,mixed> */
public function view(string $sector): array
{
$data = $this->data($sector);
$mining = $sector === 'mineracao';
$groups = $mining ? $data['minerals'] : $data['groups'];
$risks = $data['risks'];
if ($mining) {
$start = array_find_key($risks, fn ($text) => str_starts_with($text, 'Riscos e pontos'));
$end = array_find_key($risks, fn ($text) => str_starts_with($text, 'Próximos passos'));
$risks = $start !== null && $end !== null ? array_slice($risks, $start + 1, $end - $start - 1) : [];
}
return ['sector_slug' => $sector, 'title' => $mining ? 'Mineração e cadeias de valor' : 'Energia e cadeias de valor',
'introduction' => $mining ? 'Explore recursos minerais por localização e possibilidades de fornecimento, logística, energia e transformação. São referências para preparar o investimento; uma ocorrência não identifica uma concessão ou um projecto disponível.' : 'Explore gás natural, electricidade, renováveis e as actividades da cadeia de valor. Distinga projectos de referência, propostas, previsões e possibilidades de fornecimento ao preparar o investimento.',
'group_label' => $mining ? 'Grupo mineral' : 'Segmento energético',
'resource_heading' => $mining ? 'Recursos e localização' : 'Segmentos e âmbito territorial',
'resource_notice' => $mining ? 'A localização é a indicada para o grupo de minérios. Quando a fonte não discrimina a distribuição de cada minério, não se presume que todos ocorram em todos os locais.' : 'Bacias, localidades, corredores e províncias conservam o âmbito indicado na fonte. Uma localização não delimitada fica por definir no investimento; uma referência não significa concurso aberto ou projecto disponível.',
'source_sha256' => self::SOURCES[$sector], 'reference_date' => $data['reference_date'],
'groups' => array_map(fn ($group) => [...array_diff_key($group, ['province_names' => true]), 'location_labels' => $group['location_labels'] ?? $group['province_names']], $groups),
'areas' => array_map(fn ($area) => [...array_diff_key($area, ['source_fields' => true, 'province_names' => true, 'mineral_ids' => true]), 'location_labels' => $area['location_labels'] ?? $area['province_names'], 'group_ids' => $area['group_ids'] ?? $area['mineral_ids']], $data['areas']),
'projects' => $data['projects'], 'indicators' => array_map(fn ($row) => ['label' => $row['mineral'], 'value' => $row['value'], 'source' => $row['source']], $data['indicators']),
'risks' => $risks, 'sources' => $data['sources'], 'source_links' => $data['source_links'] ?? [], 'background' => $data['background'] ?? []];
}
/** @param array<string,mixed> $input
* @return array<string,mixed> */
public function selection(string $sector, array $input): array
{
abort_unless(isset(self::SOURCES[$sector]), 422);
$input = Validator::make($input, ['area_id' => 'required|string', 'source_sha256' => 'required|string|size:64', 'location_label' => 'nullable|string|max:150', 'group_id' => 'nullable|string'])->validate();
abort_unless(hash_equals(self::SOURCES[$sector], $input['source_sha256']), 409, 'A referência foi actualizada; recupere a selecção actual.');
$data = $this->view($sector);
$area = array_find($data['areas'], fn ($row) => $row['id'] === $input['area_id']);
abort_unless($area, 422, 'Escolha uma área de referência existente.');
$group = isset($input['group_id']) ? array_find($data['groups'], fn ($row) => $row['id'] === $input['group_id']) : null;
abort_if(isset($input['group_id']) && (! $group || ! in_array($group['id'], $area['group_ids'], true)), 422, 'O grupo escolhido não corresponde à cadeia de valor.');
$location = $input['location_label'] ?? null;
abort_if($location !== null && ! in_array($location, $area['location_labels'], true), 422, 'A localização escolhida não consta desta área de referência.');
$snapshot = ['sector_slug' => $sector, 'source_sha256' => self::SOURCES[$sector], 'reference_date' => $data['reference_date'],
'group_id' => $group['id'] ?? null, 'group_name' => $group['name'] ?? null, 'area_id' => $area['id'], 'title' => $area['title'], 'location_label' => $location,
'locations_text' => $area['locations_text'], 'indicative_location' => $area['indicative_location'], 'value_chain' => $area['value_chain'], 'market_evidence' => $area['market_evidence'],
'source_row' => $area['source_row'], 'source_state' => $area['source_state'], 'coordinates' => null, 'promotion_ready' => false,
'territory_precision' => $location === null ? 'source_area_reference' : ($location === 'Maputo' ? 'province_or_city_unspecified' : 'source_location_reference'),
'href' => '/sectores/'.$sector.'?'.http_build_query(['resource_area' => $area['id'], 'resource_location' => $location, 'resource_group' => $group['id'] ?? null])];
return ['key' => hash('sha256', json_encode([$sector, self::SOURCES[$sector], $area['id'], $location, $group['id'] ?? null], JSON_THROW_ON_ERROR)), ...$snapshot];
}
}