using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using System.Net.NetworkInformation;
using OpcPks.Core.Models;
using System.Diagnostics;

namespace OpcPks.Core.Services
{
    public class CertificateGenerator
    {
        private readonly string _baseDataPath;
        private readonly OpcSessionManager _sessionManager;

        public CertificateGenerator(string baseDataPath, OpcSessionManager sessionManager)
        {
            _baseDataPath = baseDataPath;
            _sessionManager = sessionManager;
        }

        public async Task<bool> CreateHoneywellCertificateAsync(CertRequestModel model)
        {
            try
            {
                string appName = "OpcPksClient";
                // [중요] 호스트네임은 대문자로 처리 (윈도우 기본값 준수)
                string hostName = Environment.MachineName.ToUpper();
                string applicationUri = $"urn:{hostName}:{appName}"; 
                
                string derPath = Path.Combine(_baseDataPath, "own", "certs", $"{appName}.der");
                string pfxPath = Path.Combine(_baseDataPath, "own", "private", $"{appName}.pfx");

                Directory.CreateDirectory(Path.GetDirectoryName(derPath)!);
                Directory.CreateDirectory(Path.GetDirectoryName(pfxPath)!);

                using var rsa = RSA.Create(2048);
                // [교정] 오직 CN만 포함 (Configuration Error 방지를 위해 불필요한 필드 제거)
                var request = new CertificateRequest(
                    $"CN={appName}", 
                    rsa, 
                    HashAlgorithmName.SHA256, 
                    RSASignaturePadding.Pkcs1);

                // 1. Subject Key Identifier (필수 지문)
                request.CertificateExtensions.Add(
                    new X509SubjectKeyIdentifierExtension(request.PublicKey, false));

                // 2. SAN (URI와 DNS 단 하나씩만 - 하니웰의 해석 오류 방지)
                var sanBuilder = new SubjectAlternativeNameBuilder();
                sanBuilder.AddUri(new Uri(applicationUri));
                sanBuilder.AddDnsName(hostName);
                request.CertificateExtensions.Add(sanBuilder.Build());

                // 3. Key Usage (가장 표준적인 3종으로 축소)
                // NonRepudiation 플래그가 가끔 BadConfiguration을 유발하므로 뺍니다.
                request.CertificateExtensions.Add(new X509KeyUsageExtension(
                    X509KeyUsageFlags.DigitalSignature | 
                    X509KeyUsageFlags.KeyEncipherment | 
                    X509KeyUsageFlags.DataEncipherment, 
                    true));

                // 4. Enhanced Key Usage
                request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(
                    new OidCollection { 
                        new Oid("1.3.6.1.5.5.7.3.1"), // Server Auth
                        new Oid("1.3.6.1.5.5.7.3.2")  // Client Auth
                    }, false));

                // 5. Basic Constraints (End Entity 명시)
                request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true));

                // [교정] 시작 시간을 1시간 전으로 설정하여 하니웰 서버와의 시간차 방어
                using var certificate = request.CreateSelfSigned(
                    DateTimeOffset.UtcNow.AddHours(-1), 
                    DateTimeOffset.UtcNow.AddYears(5));

                await File.WriteAllBytesAsync(pfxPath, certificate.Export(X509ContentType.Pfx));
                await File.WriteAllBytesAsync(derPath, certificate.Export(X509ContentType.Cert));

                Console.WriteLine($"\n--- [수동 작업 지시: mngr 계정 모드] ---");
                Console.WriteLine($"1. 파일 생성 완료: {derPath}");
                Console.WriteLine($"2. 하니웰 서버의 Trusted/certs 폴더에 기존 파일을 지우고 새 파일을 넣으세요.");
                Console.WriteLine($"3. 'mngr' 계정의 권한으로 복사가 완료되면 엔터를 치세요.");
                Console.ReadLine();

                string primaryIp = model.PrimaryIpA?.Trim() ?? "";
                // 바로 접속 테스트 진행
                return await _sessionManager.SayHelloAsync(primaryIp);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ 작업 실패: {ex.Message}");
                throw;
            }
        }
    }
}