using Application.Abstractions.Data;
using Domain.Entities.Stocks;
using Microsoft.EntityFrameworkCore;
using SharedKernel;
namespace Infrastructure.StockData;
///
/// KOSIS 파싱 결과 → MacroIndicator upsert (UQ = Code+Period). HTTP 와 분리된 순수 DB 반영 로직 (Seibro *Import 방식 미러링) —
/// 통합 테스트가 파서 fixture 입력으로 직접 호출한다. 호출부(KosisSyncService)는 지표 하나의 응답 rows 를 넘긴다.
/// 계열 특정: config ClassCode(C1) 가 지정되면 그 C1 행만, 비면 응답 첫 계열(첫 등장 C1)로 자동 고정해 여러 분류 혼입을 배제한다.
///
public static class KosisImport
{
///
/// 지표 하나의 파싱 rows 를 (Code, Period) upsert. 반환 = (inserted, updated). SaveChanges 는 호출부가 수행하지 않으므로 여기서 저장한다.
///
public static async Task<(int Inserted, int Updated)> UpsertAsync(
IAppDbContext db,
AppSettings.KosisSection.KosisIndicator indicator,
IReadOnlyList rows,
CancellationToken ct
) {
var selected = SelectSeries(indicator.ClassCode, rows);
if (selected.Count == 0)
{
return (0, 0);
}
var code = indicator.Code.Trim();
var periods = selected.Select(c => c.Period).ToHashSet();
var existing = await db.MacroIndicator.Where(c => c.Code == code && periods.Contains(c.Period)).ToListAsync(ct);
var existingByPeriod = existing.ToDictionary(c => c.Period);
var inserted = 0;
var updated = 0;
foreach (var row in selected)
{
if (existingByPeriod.TryGetValue(row.Period, out var entity))
{
var before = (entity.Value, entity.Unit, entity.Name);
entity.Update(indicator.Name, row.Value, row.Unit);
if (before != (entity.Value, entity.Unit, entity.Name))
{
updated++;
}
}
else
{
var created = MacroIndicator.Create(code, indicator.Name, row.Period, row.Value, row.Unit);
await db.MacroIndicator.AddAsync(created, ct);
existingByPeriod[row.Period] = created;
inserted++;
}
}
if (inserted > 0 || updated > 0)
{
await db.SaveChangesAsync(ct);
}
return (inserted, updated);
}
///
/// 여러 분류가 섞인 rows 에서 원하는 한 계열만 고른다. classCode 지정 시 그 C1 행만,
/// 비면 첫 등장 C1 값으로 고정 (KOSIS 통계표가 단일 계열이면 그대로 전부 채택). 시점(Period)당 1행이 되도록 첫 매치만 취한다.
///
private static List SelectSeries(string classCode, IReadOnlyList rows)
{
var result = new List();
if (rows.Count == 0)
{
return result;
}
var target = classCode.Trim();
if (target.Length == 0)
{
target = rows[0].ClassCode;
}
var seenPeriods = new HashSet();
foreach (var row in rows)
{
if (row.ClassCode != target)
{
continue;
}
if (seenPeriods.Add(row.Period))
{
result.Add(row);
}
}
return result;
}
}