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

namespace OpcPks.Core.Services
{
    public class HoneywellCrawler
    {
        // ✅ Session 대신 ISession 인터페이스를 사용하여 호환성 확보
        private readonly ISession _session;
        private readonly List<TagMaster> _discoveredTags = new();

        public HoneywellCrawler(ISession session)
        {
            _session = session ?? throw new ArgumentNullException(nameof(session));
        }

        /// <summary>
        /// 탐사 시작점
        /// </summary>
        public async Task RunAsync(string startNodeId, string csvPath)
        {
            Console.WriteLine($"\n🚀 하니웰 자산모델 탐사 시작: {startNodeId}");
            
            try 
            {
                _discoveredTags.Clear();
                NodeId rootNode = NodeId.Parse(startNodeId);
                await BrowseRecursiveAsync(rootNode, 0);

                Console.WriteLine("\n===============================================");
                Console.WriteLine($"✅ 탐사 완료! 총 {_discoveredTags.Count}개의 항목 발견.");
                Console.WriteLine("===============================================");
                
                SaveToCsv(csvPath);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ 탐사 중 오류: {ex.Message}");
            }
        }

        private async Task BrowseRecursiveAsync(NodeId nodeId, int level)
        {
            try
            {
                BrowseDescription description = new() {
                    NodeId = nodeId,
                    BrowseDirection = BrowseDirection.Forward,
                    ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
                    IncludeSubtypes = true,
                    NodeClassMask = (uint)(NodeClass.Variable | NodeClass.Object),
                    ResultMask = (uint)BrowseResultMask.All
                };

                // BrowseAsync 호출
                BrowseResponse response = await _session.BrowseAsync(null, null, 0, new BrowseDescriptionCollection { description }, default);

                if (response?.Results == null || response.Results.Count == 0) return;

                foreach (var result in response.Results)
                {
                    await ProcessReferencesAsync(result.References, level);

                    byte[]? cp = result.ContinuationPoint;
                    while (cp != null && cp.Length > 0)
                    {
                        // ✅ ByteStringCollection을 사용하여 라이브러리 표준 인자 타입 일치화
                        var cpCollection = new ByteStringCollection { cp };
                        BrowseNextResponse nextResponse = await _session.BrowseNextAsync(null, false, cpCollection, default);
                        
                        if (nextResponse?.Results != null && nextResponse.Results.Count > 0)
                        {
                            await ProcessReferencesAsync(nextResponse.Results[0].References, level);
                            cp = nextResponse.Results[0].ContinuationPoint;
                        }
                        else 
                        { 
                            cp = null; 
                        }
                    }
                }
            }
            catch (Exception) { /* 접근 권한 없는 노드는 무시 */ }
        }

        private async Task ProcessReferencesAsync(ReferenceDescriptionCollection references, int level)
        {
            if (references == null || references.Count == 0) return;

            foreach (var rd in references)
            {
                // ExpandedNodeId -> NodeId 변환
                NodeId childId = ExpandedNodeId.ToNodeId(rd.NodeId, _session.NamespaceUris);

                // TagMaster 모델 규격에 맞춰 데이터 추가
                _discoveredTags.Add(new TagMaster
                {
                    TagName = rd.BrowseName.Name ?? "Unknown",
                    FullNodeId = childId.ToString(),
                    NodeClass = rd.NodeClass.ToString(),
                    Level = level
                });

                string indent = new string(' ', level * 2);
                Console.WriteLine($"{indent} [{rd.NodeClass}] {rd.BrowseName.Name} (ID: {childId})");

                // 하위 폴더(Object)가 있고 탐사 깊이가 5단계 미만이면 재귀 탐색
                if (rd.NodeClass == NodeClass.Object && level < 5)
                {
                    await BrowseRecursiveAsync(childId, level + 1);
                }
            }
        }

        private void SaveToCsv(string filePath)
        {
            try
            {
                // 디렉토리가 없으면 생성
                string? dir = Path.GetDirectoryName(filePath);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                using StreamWriter sw = new(filePath);
                sw.WriteLine("Level,Class,Name,NodeId");
                foreach (var tag in _discoveredTags)
                {
                    sw.WriteLine($"{tag.Level},{tag.NodeClass},{tag.TagName},{tag.FullNodeId}");
                }
                Console.WriteLine($"💾 CSV 저장 완료: {filePath}");
            }
            catch (Exception ex) 
            { 
                Console.WriteLine($"❌ CSV 저장 실패: {ex.Message}"); 
            }
        }
    }
}