using ExperionCrawler.Core.Application.Interfaces;
using ExperionCrawler.Core.Domain.Entities;
using ExperionCrawler.Infrastructure.Certificates;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Client;

namespace ExperionCrawler.Infrastructure.OpcUa;

/// <summary>
/// OPC UA ApplicationConfiguration 생성을 위한 공유 설정 공급자.
/// 동일한 인스턴스를 사용하면 여러 세션이 충돌하지 않습니다.
/// </summary>
public class OpcUaConfigProvider : IOpcUaConfigProvider
{
    private readonly ILogger<OpcUaConfigProvider> _logger;

    public OpcUaConfigProvider(ILogger<OpcUaConfigProvider> logger)
    {
        _logger = logger;
    }

    /// <summary>
    /// ExperionServerConfig에 기반한 ApplicationConfiguration을 생성합니다.
    /// 여러 세션에서 동일한 인스턴스를 사용할 경우 채널 충돌 방지.
    /// </summary>
    public async Task<ApplicationConfiguration> GetConfigAsync(ExperionServerConfig cfg)
    {
        var clientCert = ExperionCertificateService.TryLoadCertificate(cfg.ClientHostName);

        var config = new ApplicationConfiguration
        {
            ApplicationName = "ExperionCrawlerClient",
            ApplicationType = ApplicationType.Client,
            ApplicationUri  = cfg.ApplicationUri,
            SecurityConfiguration = new SecurityConfiguration
            {
                ApplicationCertificate = clientCert != null
                    ? new CertificateIdentifier { Certificate = clientCert }
                    : new CertificateIdentifier(),

                TrustedPeerCertificates = new CertificateTrustList
                    { StoreType = "Directory", StorePath = Path.GetFullPath("pki/trusted") },
                TrustedIssuerCertificates = new CertificateTrustList
                    { StoreType = "Directory", StorePath = Path.GetFullPath("pki/issuers") },
                RejectedCertificateStore = new CertificateTrustList
                    { StoreType = "Directory", StorePath = Path.GetFullPath("pki/rejected") },
                AutoAcceptUntrustedCertificates = true,
                AddAppCertToTrustedStore        = true
            },
            TransportQuotas     = new TransportQuotas    { OperationTimeout      = 15_000 },
            ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60_000 }
        };

        await config.ValidateAsync(ApplicationType.Client);

        // BadCertificateChainIncomplete 등 인증서 체인 검증 오류를 허용
        // (Honeywell 서버 인증서는 자체 CA 발급으로 체인이 불완전하게 보일 수 있음)
        config.CertificateValidator.CertificateValidation += (_, e) =>
        {
            e.Accept = true;
        };

        _logger.LogDebug("[OpcUaConfig] ApplicationConfiguration 생성 완료 (ApplicationUri: {Uri})", cfg.ApplicationUri);
        return config;
    }
}