83 lines
6.7 KiB
PHP
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];
|
|
}
|
|
}
|