using Opc.Ua;
using Opc.Ua.Client;
using System.Security.Cryptography.X509Certificates;
using System.Net;

namespace OpcPks.Core.Services
{
    public class OpcSessionManager
    {
        private ISession? _session;
        public ISession? Session => _session;

        public async Task<ISession> GetSessionAsync()
        {
            if (_session != null && _session.Connected) return _session;

            string serverHostName = "192.168.0.20";
            string endpointUrl = $"opc.tcp://{serverHostName}:4840";
            string applicationUri = "urn:dbsvr:OpcTestClient"; 
            string userName = "mngr";
            string password = "mngr";
            string pfxPassword = ""; // openssl에서 확인한 비밀번호가 있다면 입력

            string baseDataPath = "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/Data/pki";
            string pfxFilePath = Path.Combine(baseDataPath, "own/private/OpcTestClient.pfx");

            var config = new ApplicationConfiguration {
                ApplicationName = "OpcTestClient",
                ApplicationType = ApplicationType.Client,
                ApplicationUri = applicationUri,
                SecurityConfiguration = new SecurityConfiguration {
                    ApplicationCertificate = new CertificateIdentifier { 
                        StoreType = "Directory", 
                        StorePath = Path.Combine(baseDataPath, "own"),
                        SubjectName = "OpcTestClient" 
                    },
                    TrustedPeerCertificates = new CertificateTrustList { 
                        StoreType = "Directory", 
                        StorePath = Path.Combine(baseDataPath, "trusted") 
                    },
                    AutoAcceptUntrustedCertificates = true,
                    AddAppCertToTrustedStore = true
                },
                TransportQuotas = new TransportQuotas { OperationTimeout = 60000 },
                ClientConfiguration = new ClientConfiguration { DefaultSessionTimeout = 60000 }
            };

            // 1. [인증서 강제 로드] Validate 호출 전에 인증서를 명시적으로 로드합니다.
            if (!File.Exists(pfxFilePath)) {
                throw new Exception($"❌ PFX 파일을 찾을 수 없습니다: {pfxFilePath}");
            }

            try {
                // 리눅스 .NET 호환용 플래그
                var cert = new X509Certificate2(
                    pfxFilePath, 
                    pfxPassword, 
                    X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet
                );
                
                // config에 인증서 직접 주입
                config.SecurityConfiguration.ApplicationCertificate.Certificate = cert;
                Console.WriteLine($"✅ [인증서 로드] {cert.Subject}");
            } catch (Exception ex) {
                Console.WriteLine($"❌ [인증서 에러] PFX 로드 실패: {ex.Message}");
                throw;
            }

            // 2. 설정 검증 (인증서 주입 후 실행)
            await config.Validate(ApplicationType.Client);
            
            // Validate 이후 인증서가 풀렸을 경우를 대비해 다시 확인
            if (config.SecurityConfiguration.ApplicationCertificate.Certificate == null) {
                Console.WriteLine("⚠️ [경고] Validate 과정에서 인증서 설정이 유실되어 재할당합니다.");
                config.SecurityConfiguration.ApplicationCertificate.Certificate = new X509Certificate2(pfxFilePath, pfxPassword, X509KeyStorageFlags.MachineKeySet);
            }

            config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = true; };

            // 3. 엔드포인트 선택 및 보안 정책 로그
            var endpointDescription = CoreClientUtils.SelectEndpoint(endpointUrl, true);
            var endpointConfiguration = EndpointConfiguration.Create(config);
            var configuredEndpoint = new ConfiguredEndpoint(null, endpointDescription, endpointConfiguration);

            Console.WriteLine($"📡 [세션] 연결 시도: {endpointUrl}");
            Console.WriteLine($"🔐 [보안] {endpointDescription.SecurityPolicyUri} ({endpointDescription.SecurityMode})");

            // 4. 세션 생성 시도
            try {
                _session = await Opc.Ua.Client.Session.Create(
                    config,
                    configuredEndpoint,
                    true, 
                    "OpcPksSession",
                    60000,
                    new UserIdentity(userName, password),
                    null
                );
                Console.WriteLine("🚀 [성공] 하니웰 서버와 연결되었습니다!");
            } catch (ServiceResultException srex) {
                Console.WriteLine($"❌ [OPC 에러] {srex.StatusCode}: {srex.Message}");
                throw;
            }

            return _session;
        }
    }
}