using Application.Abstractions.Data;
using Microsoft.EntityFrameworkCore;
namespace Application.Abstractions.Channels;
///
/// URL 파라미터(@handle 또는 YouTube Channel ID)를 내부 Channel.SID 로 변환하는 헬퍼
/// - `@` 로 시작하면 현재 핸들 → SID 로 조회
/// - 그 외에는 SID 로 간주 (YouTube Channel ID 24자)
/// - 현재 핸들로 일치하는 채널이 없으면 이전 핸들 이력 조회 후 현재 핸들/SID 반환
///
public static class ChannelIdentifierResolver
{
public enum IdentifierKind
{
Handle,
Sid
}
///
/// 가 null 이면 해당 식별자에 대응하는 채널이 없음을 의미.
/// 이 입력 핸들과 다르면 이전 핸들에서 변경된 채널임을 의미 (리다이렉트 대상).
///
public readonly record struct ResolveResult(
string? ChannelSID,
string? CanonicalHandle,
IdentifierKind Kind
);
public static IdentifierKind DetectKind(string identifier)
{
if (!string.IsNullOrEmpty(identifier) && identifier[0] == '@')
{
return IdentifierKind.Handle;
}
return IdentifierKind.Sid;
}
public static string NormalizeHandle(string identifier)
{
if (string.IsNullOrEmpty(identifier))
{
return string.Empty;
}
return identifier[0] == '@' ? identifier[1..] : identifier;
}
///
/// 입력 식별자를 Channel.SID 로 정규화.
/// - SID 입력: DB 조회 없이 그대로 반환 (존재 여부는 후속 Handler 가 검증)
/// - Handle 입력: 현재 핸들과 일치하는 채널 조회, 없으면 이력 테이블 조회하여 현재 핸들 반환
///
public static async Task ResolveAsync(IAppDbContext db, string identifier, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(identifier))
{
return new ResolveResult(null, null, IdentifierKind.Sid);
}
var kind = DetectKind(identifier);
if (kind == IdentifierKind.Sid)
{
return new ResolveResult(identifier, null, IdentifierKind.Sid);
}
var handle = NormalizeHandle(identifier);
var current = await db.Channel.AsNoTracking()
.Where(c => c.Handle == handle)
.Select(c => new { c.SID, c.Handle })
.FirstOrDefaultAsync(ct);
if (current is not null)
{
return new ResolveResult(current.SID, current.Handle, IdentifierKind.Handle);
}
// 핸들 변경 이력 조회 — 이전 핸들이면 현재 핸들/SID 반환
var historical = await db.ChannelHandleHistory.AsNoTracking()
.Where(h => h.OldHandle == handle)
.OrderByDescending(h => h.ChangedAt)
.Select(h => new { h.Channel.SID, h.Channel.Handle })
.FirstOrDefaultAsync(ct);
if (historical is not null)
{
return new ResolveResult(historical.SID, historical.Handle, IdentifierKind.Handle);
}
return new ResolveResult(null, null, IdentifierKind.Handle);
}
}