using Microsoft.AspNetCore.Mvc;
using Npgsql;
using OpcPks.Core.Data;
using OpcPks.Core.Services;
using OpcPks.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading.Tasks;

namespace OpcPks.Web.Controllers;

[Route("Engineering")]
public class EngineeringController : Controller
{
    // [현장 설정] 하니웰 데이터 및 PKI 경로 고정
    private readonly string _basePath = "/home/pacer/projects/OpcPksPlatform/OpcPks.Core/Data";
    
    // [수정] 의존성 주입을 위한 필드 추가
    private readonly CertificateGenerator _certGenerator;
    private readonly OpcSessionManager _sessionManager;

    // [수정] 생성자를 통해 Program.cs에서 등록된 서비스를 주입받음
    public EngineeringController(CertificateGenerator certGenerator, OpcSessionManager sessionManager)
    {
        _certGenerator = certGenerator;
        _sessionManager = sessionManager;
    }

    #region [기존 기능: 태그 탐사 및 관리]

    [HttpGet("TagExplorer")]
    public IActionResult TagExplorer() => View();

    [HttpGet("Admin")]
    public IActionResult Admin() => View();

    [HttpPost("SearchByFilter")]
    public async Task<IActionResult> SearchByFilter([FromBody] SearchRequest request)
    {
        var results = new List<object>();
        if (request?.Suffixes == null || request.Suffixes.Count == 0) return Json(results);

        using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
        await conn.OpenAsync();

        var suffixConditions = string.Join(" OR ", request.Suffixes.Select((s, i) => $"node_id ILIKE @s{i}"));
        var sql = $@"SELECT name, node_id, data_type FROM raw_node_map 
                     WHERE name ILIKE @tagTerm AND ({suffixConditions}) 
                     ORDER BY name ASC LIMIT 1500";

        using var cmd = new NpgsqlCommand(sql, conn);
        cmd.Parameters.AddWithValue("tagTerm", $"%{request.TagTerm}%");
        for (int i = 0; i < request.Suffixes.Count; i++)
            cmd.Parameters.AddWithValue($"s{i}", $"%{request.Suffixes[i]}");

        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync()) {
            results.Add(new {
                name = reader.GetString(0),
                nodeId = reader.GetString(1),
                dataType = reader.IsDBNull(2) ? "Double" : reader.GetString(2)
            });
        }
        return Json(results);
    }

    [HttpPost("RegisterTags")]
    public async Task<IActionResult> RegisterTags([FromBody] List<TagRegistrationRequest> tags)
    {
        if (tags == null || tags.Count == 0) return Ok();
        using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
        await conn.OpenAsync();
        using var trans = await conn.BeginTransactionAsync();
        try {
            var masterSql = @"INSERT INTO tag_master (server_name, area_code, tag_name, parameter, full_node_id, data_type)
                              VALUES (@server, @area, @tag, @param, @nodeId, @type)
                              ON CONFLICT (full_node_id) DO UPDATE SET data_type = EXCLUDED.data_type;";

            var liveSql = @"INSERT INTO tag_live_data (full_node_id, live_value, quality)
                            VALUES (@nodeId, '0', 'Initial')
                            ON CONFLICT (full_node_id) DO NOTHING;";

            foreach (var tag in tags) {
                if (string.IsNullOrEmpty(tag.NodeId)) continue;

                string sContent = tag.NodeId.Contains("s=") ? tag.NodeId.Split("s=")[1] : tag.NodeId;
                string[] parts = sContent.Split(':');
                string server = parts[0];
                string area = parts.Length >= 3 ? parts[1] : "unassigned";
                string remains = parts.Last();
                int lastDot = remains.LastIndexOf('.');
                string tagName = (lastDot != -1) ? remains.Substring(0, lastDot) : remains;
                string param = (lastDot != -1) ? remains.Substring(lastDot + 1) : "pv";

                using (var cmd = new NpgsqlCommand(masterSql, conn, trans)) {
                    cmd.Parameters.AddWithValue("server", server);
                    cmd.Parameters.AddWithValue("area", area);
                    cmd.Parameters.AddWithValue("tag", tagName);
                    cmd.Parameters.AddWithValue("param", param);
                    cmd.Parameters.AddWithValue("nodeId", tag.NodeId);
                    cmd.Parameters.AddWithValue("type", tag.DataType ?? "Double");
                    await cmd.ExecuteNonQueryAsync();
                }
                using (var cmd = new NpgsqlCommand(liveSql, conn, trans)) {
                    cmd.Parameters.AddWithValue("nodeId", tag.NodeId);
                    await cmd.ExecuteNonQueryAsync();
                }
            }
            await trans.CommitAsync();
            return Ok();
        } catch (Exception ex) {
            await trans.RollbackAsync();
            return BadRequest(ex.Message);
        }
    }

    [HttpPost("RunCrawler")]
    public async Task<IActionResult> RunCrawler()
    {
        try {
            // [수정] new 생성 대신 주입된 _sessionManager 사용
            // GetSessionAsync에 필요한 인자(IP) 전달 (기본값 또는 모델 활용)
            var session = await _sessionManager.GetSessionAsync("192.168.0.20", new CertRequestModel()); 
            if (session == null || !session.Connected)
                return BadRequest(new { message = "하니웰 서버 연결 실패." });

            var crawler = new HoneywellCrawler(session);
            string csvPath = Path.Combine(_basePath, "Honeywell_FullMap.csv");
            await crawler.RunAsync("ns=1;s=$assetmodel", csvPath);
            return Ok(new { message = "탐사 및 CSV 생성 완료!" });
        }
        catch (Exception ex) { return BadRequest(new { message = ex.Message }); }
    }

    [HttpPost("ImportCsv")]
    public async Task<IActionResult> ImportCsv()
    {
        try {
            using var conn = new NpgsqlConnection(DbConfig.ConnectionString);
            await conn.OpenAsync();
            string csvPath = Path.Combine(_basePath, "Honeywell_FullMap.csv");
            var sql = $@"TRUNCATE raw_node_map;
                         COPY raw_node_map(level, node_class, name, node_id) 
                         FROM '{csvPath}' 
                         DELIMITER ',' CSV HEADER;";
            using var cmd = new NpgsqlCommand(sql, conn);
            await cmd.ExecuteNonQueryAsync();
            return Ok(new { message = "DB 동기화 완료" });
        }
        catch (Exception ex) { return BadRequest(ex.Message); }
    }

    #endregion

    #region [안전 강화 기능: 하니웰 전용 인증서 관리]

    [HttpGet("CertManager")]
    public IActionResult CertManager() 
    {
        // [약속] 통일된 경로 확인 (CertificateGenerator 내부 경로와 일치시킴)
        string pfxPath = Path.Combine(_basePath, "own/private/OpcPksClient.pfx");
        
        ViewBag.IsCertExists = System.IO.File.Exists(pfxPath);
        ViewBag.SuccessMsg = TempData["Success"];
        ViewBag.ErrorMsg = TempData["Error"];
        
        return View(new CertRequestModel()); 
    }

    [HttpPost("GenerateCertificate")]
    public async Task<IActionResult> GenerateCertificate(CertRequestModel model)
    {
        try 
        {
            // [수정] 직접 new 생성하던 코드를 지우고 주입된 _certGenerator 사용
            // 이제 이 호출은 내부적으로 하니웰 서버와 3회 밀당을 수행합니다.
            var success = await _certGenerator.CreateHoneywellCertificateAsync(model);
            
            if (success) {
                TempData["Success"] = "인증서 생성 및 하니웰 서버 최종 수용 확인 완료!";
                return RedirectToAction("CertManager");
            }
            
            TempData["Error"] = "인증서 전송 과정에서 오류가 발생했습니다.";
            return RedirectToAction("CertManager");
        }
        catch (Exception ex) 
        {
            Console.WriteLine($"[FATAL_CERT] {DateTime.Now}: {ex.Message}");
            TempData["Error"] = $"시스템 오류: {ex.Message}";
            return RedirectToAction("CertManager");
        }
    }

    #endregion

    public class SearchRequest 
    { 
        public string TagTerm { get; set; } = string.Empty; 
        public List<string> Suffixes { get; set; } = new(); 
    }
    
    public class TagRegistrationRequest 
    { 
        public string TagName { get; set; } = string.Empty; 
        public string NodeId { get; set; } = string.Empty; 
        public string DataType { get; set; } = "Double"; 
    }
}