@startuml
skinparam style strictuml
skinparam defaultFontName "DM Sans"
skinparam defaultFontSize 14
skinparam titleFontName "Volkhov"
skinparam titleFontSize 20
skinparam backgroundColor #FAFCF9
skinparam defaultFontColor #28342A
skinparam titleFontColor #28342A
skinparam ArrowColor #628A5B
skinparam NoteBackgroundColor #EAF0E8
skinparam NoteBorderColor #547461
skinparam ActivityBackgroundColor #FAFCF9
skinparam ActivityBorderColor #547461
skinparam ActivityDiamondBackgroundColor #FAFCF9
skinparam ActivityDiamondBorderColor #628A5B
skinparam ActivityBarColor #628A5B
skinparam SwimlaneBorderColor #547461
skinparam SwimlaneBorderThickness 0.3

title The Biergarten Data Pipeline (Streaming Architecture)

|#F2F6F0|main.cc|
start
:ParseArguments(argc, argv);
if (Are arguments valid?) then (no)
  :spdlog::error usage info;
  stop
else (yes)
endif

:Init CurlGlobalState & LlamaBackendState;
:di::make_injector(...);
:injector.create<std::unique_ptr<BiergartenDataGenerator>>();
:BiergartenDataGenerator::Run();

|#EAF0E8|BiergartenDataGenerator|
:Initialize SQLite export;

|#E0EAE0|SqliteExportService|
:GetUtcTimestamp() from SystemDateTimeProvider;
:Initialize();
note right
  Builds a fresh biergarten_seed_<UTC datetime>.sqlite filename
  Appends a numeric suffix if the timestamp already exists
  Opens DB Connection
  Executes Schema DDL
  Begins Transaction
end note

|#EAF0E8|BiergartenDataGenerator|
:QueryCitiesWithCountries();

|#E2EBDC|JsonLoader|
:JsonLoader::LoadLocations("locations.json");
:std::ranges::sample(all_locations, 50);

|#EAF0E8|BiergartenDataGenerator|
while (For each sampled Location?) is (Remaining cities)
  |#DCE8D8|WikipediaService|
  :GetLocationContext(loc);
  :FetchExtracts(City, Country, Beer);
  |#EAF0E8|BiergartenDataGenerator|
  :Store EnrichedCity{Location, region_context};
endwhile (Done)

|#EAF0E8|BiergartenDataGenerator|
:GenerateBreweries(enriched_cities);

|#E5EDE1|DataGenerator|
while (For each EnrichedCity?) is (Remaining cities)
  if (Generator Mode) then (MockGenerator)
    :DeterministicHash & Format;
  else (LlamaGenerator)
    :PrepareRegionContext;
    :LoadBrewerySystemPrompt("prompts/system.md");
    repeat
      :Infer(system_prompt, user_prompt, max_tokens, kBreweryJsonGrammar);
      :ValidateBreweryJson(raw, brewery);
      if (Is JSON Valid?) then (yes)
        break
      else (no)
        :Attempt++;
      endif
    repeat while (Attempt < 3?) is (yes)
  endif

  |#EAF0E8|BiergartenDataGenerator|
  if (Generation successful?) then (yes)
    |#E0EAE0|SqliteExportService|
    :ProcessRecord(GeneratedBrewery);
    if (Location in cache?) then (yes)
      :Reuse location_id;
    else (no)
      :Insert Location & Cache ID;
    endif
    :Insert Brewery (FK: location_id);

    if (Exception caught during insert?) then (yes)
      |#EAF0E8|BiergartenDataGenerator|
      :spdlog::warn "Failed to stream record to SQLite export";
      note right
        Data loss is prevented per-record.
        The pipeline continues running.
      end note
    else (no)
    endif
  else (no)
    :spdlog::warn "Generation failed, skipping...";
  endif
  |#E5EDE1|DataGenerator|
endwhile (Done)

|#E0EAE0|SqliteExportService|
:Finalize();
note right
  Commits Transaction
  Closes Database Connection
end note

|#F2F6F0|main.cc|
:Return 0;
stop

@enduml
