using Opc.Ua;
using Opc.Ua.Client;
using System;
using System.Threading.Tasks;
using OpcPks.Core.Models;

namespace OpcPks.Core.Services
{
    public class OpcSessionManager
    {
        private readonly string _pkiPath = "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/Data/pki";

        /// <summary>
        /// [수정] 하니웰 인사용: 단순히 연결만 하는 게 아니라 서버 시간을 읽으려 시도하여 
        /// 하니웰 본체 엔진이 인증서를 Rejected로 던지도록 유도합니다.
        /// </summary>
        public async Task<bool> SayHelloAsync(string ip)
        {
            try
            {
                // CertRequestModel은 최소 정보만 담아 전달
                using (var session = await GetSessionAsync(ip, new CertRequestModel { PrimaryIpA = ip }))
                {
                    if (session != null && session.Connected)
                    {
                        // 연결 성공 시 서버 시간 노드를 읽어 실제 통신이 가능한지 확인
                        var currentTime = session.ReadValue(Variables.Server_ServerStatus_CurrentTime);
                        Console.WriteLine($"✅ [SayHello] 하니웰 연결 및 데이터 읽기 성공: {currentTime}");
                        
                        await session.CloseAsync();
                        return true;
                    }
                }
                return false;
            }
            catch (Exception ex)
            {
                // 여기서 에러가 나야 정상입니다. (인증서가 신뢰되지 않았을 때 하니웰이 거부하며 파일을 Rejected로 보냄)
                Console.WriteLine($"⚠️ [SayHello] 하니웰 거부 반응 유도 성공: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// [핵심] 크롤러 및 모든 서비스가 공통으로 사용하는 실제 세션 생성 로직
        /// </summary>
        public async Task<ISession?> GetSessionAsync(string ip, CertRequestModel model)
        {
            if (string.IsNullOrEmpty(ip)) return null;

            string endpointUrl = $"opc.tcp://{ip}:4840";
            
            var config = new ApplicationConfiguration()
            {
                ApplicationName = "OpcPksClient",
                ApplicationType = ApplicationType.Client,
                SecurityConfiguration = new SecurityConfiguration
                {
                    ApplicationCertificate = new CertificateIdentifier { 
                        StoreType = "Directory", 
                        StorePath = $"{_pkiPath}/own" 
                    },
                    TrustedPeerCertificates = new CertificateTrustList { 
                        StoreType = "Directory", 
                        StorePath = $"{_pkiPath}/trusted" 
                    },
                    RejectedCertificateStore = new CertificateTrustList { 
                        StoreType = "Directory", 
                        StorePath = $"{_pkiPath}/rejected" 
                    },
                    AutoAcceptUntrustedCertificates = true
                },
                TransportQuotas = new TransportQuotas { OperationTimeout = 10000 },
                ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 10000 }
            };

            await config.Validate(ApplicationType.Client);

            try
            {
                // 하니웰 보안 설정에 맞는 엔드포인트 선택
                var endpointDescription = CoreClientUtils.SelectEndpoint(endpointUrl, true, 5000);
                var endpointConfiguration = EndpointConfiguration.Create(config);
                var endpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

                // 세션 생성 시도: 이 과정 자체가 하니웰 본체에 대한 보안 컨택입니다.
                return await Session.Create(
                    config, 
                    endpoint, 
                    false, 
                    "OpcPksSession", 
                    10000, 
                    new UserIdentity(new AnonymousIdentityToken()), 
                    null);
            }
            catch (ServiceResultException sx)
            {
                Console.WriteLine($"[OPC_UA] 하니웰 보안 응답 코드: {sx.StatusCode}");
                return null;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[OPC_UA] 세션 생성 중 오류: {ex.Message}");
                return null;
            }
        }
    }
}