// Composition.swift — S2 → S3 통합 진입점 (소비모델 b) + 엔드포인트 단일소스 config.
//
// INTEGRATION (소비모델 b): 앱(S3)은 이 패키지의 `AIFabric` product 에 **로컬 SwiftPM 의존**으로 붙고,
//   Sources/AI 를 앱 타깃에 직접 포함하지 않는다(소스 이중소유/중복심볼 방지). S3 는 makeDefaultRouter(...)
//   하나로 실 라우터를 와이어링하고 MockAIProvider 를 대체한다. 구체 DSAskClient(HTTP) = S3 소유.
//
// 엔드포인트 단일소스(S2-Fa): raw URL 산재 금지 — 부주의한 편집의 침묵 엔드포인트 swap 방지
// (2026-05-17 Hermes incident 선례). env override → 검증된 기본값. ([[feedback_hermes_config_single_source_envvar]])
import Foundation
import os

public struct AIProviderConfiguration: Sendable {
    /// 맥미니 llm-router base (trailing slash 없는 base; 경로는 provider 가 append).
    public var localMLXBaseURL: URL
    /// llm-router 모델 별칭(라이브 확정 2026-06-05: 'mac-mini-default' → gemma-4-26b resolve).
    public var localMLXModel: String
    /// DS API base — S3 의 DSAskClient 가 사용. 공인 https://document.hyungi.net/api · 내부 http://100.110.63.63:8000/api.
    /// 주의: DS `/search/ask` 는 **trailing slash 필수**(경로 결합 시 S3 client 가 보장).
    public var dsBaseURL: URL
    public var requestTimeout: TimeInterval
    public var probeTimeout: TimeInterval

    public init(
        localMLXBaseURL: URL,
        localMLXModel: String = "mac-mini-default",
        dsBaseURL: URL,
        requestTimeout: TimeInterval = 60,
        probeTimeout: TimeInterval = 2
    ) {
        self.localMLXBaseURL = localMLXBaseURL
        self.localMLXModel = localMLXModel
        self.dsBaseURL = dsBaseURL
        self.requestTimeout = requestTimeout
        self.probeTimeout = probeTimeout
    }

    /// 환경변수 override → 검증된 기본값(단일 source). 키 부재 시 기본값.
    public static func resolved(
        environment: [String: String] = ProcessInfo.processInfo.environment
    ) -> AIProviderConfiguration {
        let localMLX = environment["AIFABRIC_LOCALMLX_URL"].flatMap(URL.init(string:))
            ?? URL(string: "http://100.76.254.116:8890")!
        let model = environment["AIFABRIC_LOCALMLX_MODEL"] ?? "mac-mini-default"
        let ds = environment["AIFABRIC_DS_URL"].flatMap(URL.init(string:))
            ?? URL(string: "https://document.hyungi.net/api")!
        return AIProviderConfiguration(localMLXBaseURL: localMLX, localMLXModel: model, dsBaseURL: ds)
    }
}

/// 기본 OSLog 라우팅 훅 — 폴백/스킵을 가시화(silent 금지). S3 도 참조 가능(public).
public enum AIFabricLog {
    static let router = Logger(subsystem: "ds-app.AIFabric", category: "AIRouter")
    public static let routerHook: @Sendable (String) -> Void = { msg in
        router.info("\(msg, privacy: .public)")
    }
}

/// S3 → S2 단일 진입점. 4 provider 전부 등록(vision 체인 가시 폴백 보장) + 기본 정책 + log 훅.
/// - client: S3 가 주입하는 구체 DS ask client(HTTP).
/// - config: 엔드포인트 단일소스(기본 = env override → 검증 기본값).
/// - session: LocalMLX URLSession(기본 .shared; 테스트는 mock 주입).
public func makeDefaultRouter(
    client: DSAskClient,
    config: AIProviderConfiguration = .resolved(),
    session: URLSession = .shared,
    policy: AIRoutingPolicy = .default,
    log: @escaping @Sendable (String) -> Void = AIFabricLog.routerHook
) -> AIRouter {
    let providers: [AIProviderID: any AIProvider] = [
        .remoteDS: RemoteDSProvider(client: client),
        .localMLX: LocalMLXProvider(
            baseURL: config.localMLXBaseURL,
            model: config.localMLXModel,
            session: session,
            requestTimeout: config.requestTimeout,
            probeTimeout: config.probeTimeout
        ),
        .onDevice: OnDeviceProvider(),
        .specialized: SpecializedProvider(),   // scaffold(불가) — vision 폴백 가시화
    ]
    return AIRouter(providers: providers, policy: policy, log: log)
}
