using ExperionCrawler.Core.Application.DTOs;
using ExperionCrawler.Core.Application.Interfaces;
using ExperionCrawler.Core.Application.Services;
using ExperionCrawler.Core.Domain.Entities;
using ExperionCrawler.Infrastructure.Csv;
using ExperionCrawler.Infrastructure.Database;
using Microsoft.AspNetCore.Mvc;

namespace ExperionCrawler.Web.Controllers;

// ── 인증서 ────────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/certificate")]
public class ExperionCertificateController : ControllerBase
{
    private readonly IExperionCertificateService _certSvc;

    public ExperionCertificateController(IExperionCertificateService certSvc)
        => _certSvc = certSvc;

    /// <summary>현재 인증서 상태 조회</summary>
    [HttpGet("status")]
    public IActionResult GetStatus([FromQuery] string clientHostName = "dbsvr")
    {
        var info = _certSvc.GetCertificateInfo(clientHostName);
        return Ok(new
        {
            exists      = info.Exists,
            subjectName = info.SubjectName,
            notAfter    = info.NotAfter,
            thumbPrint  = info.ThumbPrint,
            filePath    = info.FilePath
        });
    }

    /// <summary>인증서 생성 (없으면 생성, 있으면 기존 반환)</summary>
    [HttpPost("create")]
    public async Task<IActionResult> Create([FromBody] ExperionCertCreateDto dto)
    {
        var applicationUri = $"urn:{dto.ClientHostName}:ExperionCrawlerClient";
        var result = await _certSvc.EnsureCertificateAsync(
            applicationUri, dto.ClientHostName, dto.SubjectAltNames, dto.PfxPassword);

        return Ok(new
        {
            success    = result.Success,
            message    = result.Message,
            thumbPrint = result.ThumbPrint
        });
    }
}

// ── 서버 접속 테스트 ──────────────────────────────────────────────────────────

[ApiController]
[Route("api/connection")]
public class ExperionConnectionController : ControllerBase
{
    private readonly IExperionOpcClient _opcClient;

    public ExperionConnectionController(IExperionOpcClient opcClient)
        => _opcClient = opcClient;

    /// <summary>OPC UA 서버 접속 테스트</summary>
    [HttpPost("test")]
    public async Task<IActionResult> Test([FromBody] ExperionServerConfigDto dto)
    {
        var cfg = MapConfig(dto);
        var r   = await _opcClient.TestConnectionAsync(cfg);
        return Ok(new { success = r.Success, message = r.Message, sessionId = r.SessionId, policyUri = r.PolicyUri });
    }

    /// <summary>단일 노드 태그 읽기</summary>
    [HttpPost("read")]
    public async Task<IActionResult> ReadTag([FromBody] ExperionReadTagRequestDto dto)
    {
        var cfg = MapConfig(dto.ServerConfig);
        var r   = await _opcClient.ReadTagAsync(cfg, dto.NodeId);
        return Ok(new
        {
            success    = r.Success,
            nodeId     = r.NodeId,
            value      = r.Value?.ToString(),
            statusCode = r.StatusCode,
            timestamp  = r.Timestamp,
            error      = r.ErrorMessage
        });
    }

    /// <summary>노드 탐색 (ObjectsFolder 기준)</summary>
    [HttpPost("browse")]
    public async Task<IActionResult> Browse([FromBody] ExperionBrowseRequestDto dto)
    {
        var cfg = MapConfig(dto.ServerConfig);
        var r   = await _opcClient.BrowseNodesAsync(cfg, dto.StartNodeId);
        return Ok(new { success = r.Success, nodes = r.Nodes, error = r.ErrorMessage });
    }

    private static ExperionServerConfig MapConfig(ExperionServerConfigDto dto) => new()
    {
        ServerHostName = dto.ServerHostName,
        Port           = dto.Port,
        ClientHostName = dto.ClientHostName,
        UserName       = dto.UserName,
        Password       = dto.Password
    };
}

// ── 크롤링 ────────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/crawl")]
public class ExperionCrawlController : ControllerBase
{
    private readonly ExperionCrawlService _crawlSvc;

    public ExperionCrawlController(ExperionCrawlService crawlSvc)
        => _crawlSvc = crawlSvc;

    /// <summary>전체 노드맵 크롤 (재귀 탐색 → CSV 저장)</summary>
    [HttpPost("nodemap")]
    public async Task<IActionResult> NodeMap(
        [FromBody] ExperionNodeMapCrawlRequestDto dto,
        CancellationToken ct)
    {
        var cfg = new ExperionServerConfig
        {
            ServerHostName = dto.ServerConfig.ServerHostName,
            Port           = dto.ServerConfig.Port,
            ClientHostName = dto.ServerConfig.ClientHostName,
            UserName       = dto.ServerConfig.UserName,
            Password       = dto.ServerConfig.Password
        };

        var result = await _crawlSvc.RunNodeMapCrawlAsync(cfg, dto.MaxDepth, ct);

        return Ok(new
        {
            success    = result.Success,
            totalCount = result.TotalCount,
            csvPath    = result.CsvPath,
            error      = result.ErrorMessage
        });
    }

    /// <summary>크롤링 시작 (동기식 – 완료 후 결과 반환)</summary>
    [HttpPost("start")]
    public async Task<IActionResult> Start(
        [FromBody] ExperionCrawlRequestDto dto,
        CancellationToken ct)
    {
        var cfg = new ExperionServerConfig
        {
            ServerHostName = dto.ServerConfig.ServerHostName,
            Port           = dto.ServerConfig.Port,
            ClientHostName = dto.ServerConfig.ClientHostName,
            UserName       = dto.ServerConfig.UserName,
            Password       = dto.ServerConfig.Password
        };

        var result = await _crawlSvc.RunCrawlAsync(
            cfg, dto.NodeIds, dto.IntervalSeconds, dto.DurationSeconds, ct);

        return Ok(new
        {
            success      = true,
            sessionId    = result.SessionId,
            totalRecords = result.TotalRecords,
            csvPath      = result.CsvPath,
            preview      = result.Records.Take(5).Select(r => new
            {
                r.NodeId, r.Value, r.StatusCode,
                collectedAt = r.CollectedAt
            })
        });
    }
}

// ── DB 저장 ───────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/database")]
public class ExperionDatabaseController : ControllerBase
{
    private readonly ExperionCrawlService _crawlSvc;
    private readonly IExperionDbService   _dbSvc;
    private readonly IExperionCsvService  _csvSvc;
    private readonly AssetLoader          _assetLoader;

    public ExperionDatabaseController(
        ExperionCrawlService crawlSvc,
        IExperionDbService   dbSvc,
        IExperionCsvService  csvSvc,
        AssetLoader          assetLoader)
    {
        _crawlSvc    = crawlSvc;
        _dbSvc       = dbSvc;
        _csvSvc      = csvSvc;
        _assetLoader = assetLoader;
    }

    /// <summary>수집된 CSV 파일 목록</summary>
    [HttpGet("files")]
    public IActionResult GetCsvFiles()
        => Ok(new { files = _csvSvc.GetAvailableFiles() });

    /// <summary>CSV → DB 임포트 (ServerHostName 지정 시 → raw_node_map, 그 외 → ExperionRecords)</summary>
    [HttpPost("import")]
    public async Task<IActionResult> Import([FromBody] ExperionCsvImportDto dto)
    {
        if (!string.IsNullOrEmpty(dto.ServerHostName) &&
            dto.FileName.StartsWith(dto.ServerHostName, StringComparison.OrdinalIgnoreCase))
        {
            try
            {
                var fullPath    = Path.Combine("data/csv", dto.FileName);
                var rawCount    = await _assetLoader.ImportFullMapAsync(fullPath, dto.Truncate);
                var masterCount = await _dbSvc.BuildMasterFromRawAsync(dto.Truncate);
                return Ok(new
                {
                    success     = true,
                    count       = rawCount,
                    masterCount,
                    message     = $"{rawCount}개 노드 → raw_node_map 적재, {masterCount}개 → node_map_master 빌드 완료"
                });
            }
            catch (Exception ex)
            {
                return Ok(new { success = false, count = 0, masterCount = 0, message = $"오류: {ex.Message}" });
            }
        }

        var result = await _crawlSvc.ImportCsvToDbAsync(dto.FileName, dto.Truncate);
        return Ok(new { success = result.Success, count = result.Count, message = result.Message });
    }

    /// <summary>DB 레코드 조회</summary>
    [HttpGet("records")]
    public async Task<IActionResult> GetRecords(
        [FromQuery] DateTime? from,
        [FromQuery] DateTime? to,
        [FromQuery] int limit = 100)
    {
        var records = await _dbSvc.GetRecordsAsync(from, to, limit);
        var total   = await _dbSvc.GetTotalCountAsync();
        return Ok(new { total, records });
    }
}

// ── 포인트빌더 ────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/pointbuilder")]
public class ExperionPointBuilderController : ControllerBase
{
    private readonly IExperionDbService      _dbSvc;
    private readonly IExperionRealtimeService _realtimeSvc;
    public ExperionPointBuilderController(
        IExperionDbService dbSvc,
        IExperionRealtimeService realtimeSvc)
    {
        _dbSvc       = dbSvc;
        _realtimeSvc = realtimeSvc;
    }

    /// <summary>node_map_master → realtime_table 빌드 (기존 데이터 전체 교체)</summary>
    [HttpPost("build")]
    public async Task<IActionResult> Build([FromBody] PointBuilderBuildDto dto)
    {
        var count = await _dbSvc.BuildRealtimeTableAsync(dto.Names, dto.DataTypes);
        return Ok(new { success = true, count, message = $"{count}개 포인트 생성 완료" });
    }

    /// <summary>realtime_table 전체 조회</summary>
    [HttpGet("points")]
    public async Task<IActionResult> GetPoints()
    {
        var points = await _dbSvc.GetRealtimePointsAsync();
        return Ok(new
        {
            count  = points.Count(),
            points = points.Select(p => new
            {
                p.Id, p.TagName, p.NodeId, p.LiveValue,
                timestamp = p.Timestamp
            })
        });
    }

    /// <summary>node_id 를 직접 입력해서 수동 추가</summary>
    [HttpPost("add")]
    public async Task<IActionResult> Add([FromBody] PointBuilderAddDto dto)
    {
        if (string.IsNullOrWhiteSpace(dto.NodeId))
            return BadRequest(new { success = false, message = "node_id 는 필수입니다." });

        var nodeId = dto.NodeId.Trim();

        // DB에 먼저 추가
        var point = await _dbSvc.AddRealtimePointAsync(nodeId);

        // 구독 중이면 OPC UA 서버에 핫 추가 및 node_id 유효성 검증
        var (ok, msg) = await _realtimeSvc.AddMonitoredItemAsync(nodeId);
        if (!ok)
        {
            // OPC UA 서버가 거부 → DB 롤백
            await _dbSvc.DeleteRealtimePointAsync(point.Id);
            return BadRequest(new { success = false, message = msg });
        }

        return Ok(new { success = true, message = msg, point = new { point.Id, point.TagName, point.NodeId } });
    }

    /// <summary>포인트 삭제</summary>
    [HttpDelete("{id:int}")]
    public async Task<IActionResult> Delete(int id)
    {
        var deleted = await _dbSvc.DeleteRealtimePointAsync(id);
        return Ok(new { success = deleted, message = deleted ? "삭제 완료" : "포인트를 찾을 수 없습니다." });
    }
}

// ── 실시간 서비스 ─────────────────────────────────────────────────────────────

[ApiController]
[Route("api/realtime")]
public class ExperionRealtimeController : ControllerBase
{
    private readonly IExperionRealtimeService _rtSvc;
    public ExperionRealtimeController(IExperionRealtimeService rtSvc) => _rtSvc = rtSvc;

    [HttpPost("start")]
    public async Task<IActionResult> Start([FromBody] ExperionServerConfigDto dto)
    {
        var cfg = MapConfig(dto);
        await _rtSvc.StartAsync(cfg);
        return Ok(new { success = true, message = "실시간 구독 시작" });
    }

    [HttpPost("stop")]
    public async Task<IActionResult> Stop()
    {
        await _rtSvc.StopAsync();
        return Ok(new { success = true, message = "실시간 구독 중지" });
    }

    [HttpGet("status")]
    public IActionResult Status()
    {
        var s = _rtSvc.GetStatus();
        return Ok(new { running = s.Running, subscribedCount = s.SubscribedCount, message = s.Message });
    }

    private static ExperionServerConfig MapConfig(ExperionServerConfigDto dto) => new()
    {
        ServerHostName = dto.ServerHostName,
        Port           = dto.Port,
        ClientHostName = dto.ClientHostName,
        UserName       = dto.UserName,
        Password       = dto.Password
    };
}

// ── 이력 조회 ─────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/history")]
public class ExperionHistoryController : ControllerBase
{
    private readonly IExperionDbService _dbSvc;
    private readonly ILogger<ExperionHistoryController> _logger;
    
    public ExperionHistoryController(IExperionDbService dbSvc, ILogger<ExperionHistoryController> logger)
    {
        _dbSvc = dbSvc;
        _logger = logger;
    }

    /// <summary>realtime_table 의 tagname 목록</summary>
    [HttpGet("tagnames")]
    public async Task<IActionResult> TagNames()
    {
        try
        {
            _logger.LogDebug("[History] tagname 목록 조회 시작");
            var names = await _dbSvc.GetTagNamesAsync();
            var count = names.Count();
            _logger.LogDebug("[History] tagname 목록 조회 완료: {Count}개 태그", count);
            return Ok(new { tagNames = names });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[History] tagname 목록 조회 실패");
            return StatusCode(500, new { success = false, message = $"tagname 조회 실패: {ex.Message}" });
        }
    }

    /// <summary>이력 조회 (tagname 다중, 시간범위, limit)</summary>
    [HttpGet("query")]
    public async Task<IActionResult> Query(
        [FromQuery] List<string>? tagNames,
        [FromQuery] DateTime?     from,
        [FromQuery] DateTime?     to,
        [FromQuery] int           limit = 1000)
    {
        try
        {
            var tagList = tagNames ?? Enumerable.Empty<string>().ToList();
            _logger.LogDebug("[History] 이력 조회 시작 - 태그 수: {TagCount}, FROM: {From}, TO: {To}, LIMIT: {Limit}",
                tagList.Count(), from, to, limit);
            
            var result = await _dbSvc.QueryHistoryAsync(
                tagList, from, to, limit);

            _logger.LogDebug("[History] 이력 조회 완료: {RowCount}행, {TagCount}태그",
                result.Rows.Count(), result.TagNames.Count());
            
            return Ok(new
            {
                tagNames = result.TagNames,
                rows     = result.Rows.Select(r => new
                {
                    recordedAt = r.RecordedAt,
                    values     = r.Values
                })
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "[History] 이력 조회 실패 - tagNames: {@TagNames}, from: {From}, to: {To}",
                tagNames, from, to);
            return StatusCode(500, new
            {
                success = false,
                message = $"이력 조회 실패: {ex.Message}",
                detail = ex.InnerException?.Message ?? ex.StackTrace
            });
        }
    }
}

// ── OPC UA 서버 ───────────────────────────────────────────────────────────────

[ApiController]
[Route("api/opcserver")]
public class ExperionOpcServerController : ControllerBase
{
    private readonly IExperionOpcServerService _svc;
    private readonly IExperionDbService        _db;

    public ExperionOpcServerController(
        IExperionOpcServerService svc, IExperionDbService db)
    {
        _svc = svc;
        _db  = db;
    }

    /// <summary>OPC UA 서버 상태 조회</summary>
    [HttpGet("status")]
    public IActionResult Status()
    {
        var s = _svc.GetStatus();
        return Ok(new
        {
            running              = s.Running,
            connectedClientCount = s.ConnectedClientCount,
            nodeCount            = s.NodeCount,
            endpointUrl          = s.EndpointUrl,
            startedAt            = s.StartedAt
        });
    }

    /// <summary>OPC UA 서버 시작 (자동 재시작 플래그 저장)</summary>
    [HttpPost("start")]
    public async Task<IActionResult> Start()
    {
        try
        {
            await _svc.StartServerAsync();
            return Ok(new { success = true, message = "OPC UA 서버 시작 완료" });
        }
        catch (Exception ex)
        {
            return StatusCode(500, new { success = false, message = ex.Message });
        }
    }

    /// <summary>OPC UA 서버 중지 (자동 재시작 플래그 삭제)</summary>
    [HttpPost("stop")]
    public async Task<IActionResult> Stop()
    {
        await _svc.StopServerAsync();
        return Ok(new { success = true, message = "OPC UA 서버 중지 완료" });
    }

    /// <summary>주소 공간 재구성 (포인트빌더 빌드 후 호출)</summary>
    [HttpPost("rebuild")]
    public async Task<IActionResult> Rebuild()
    {
        var points = (await _db.GetRealtimePointsAsync()).ToList();
        _svc.RebuildAddressSpace(points);
        return Ok(new { success = true, nodeCount = points.Count });
    }
}

// ── 노드맵 대시보드 ───────────────────────────────────────────────────────────

[ApiController]
[Route("api/nodemap")]
public class ExperionNodeMapController : ControllerBase
{
    private readonly IExperionDbService _dbSvc;
    public ExperionNodeMapController(IExperionDbService dbSvc) => _dbSvc = dbSvc;

    /// <summary>node_map_master 의 name 컬럼 고유값 목록</summary>
    [HttpGet("names")]
    public async Task<IActionResult> Names()
    {
        var names = await _dbSvc.GetNameListAsync();
        return Ok(new { names });
    }

    /// <summary>node_map_master 통계 (총 수, 클래스별, 최대 레벨, 데이터타입 목록)</summary>
    [HttpGet("stats")]
    public async Task<IActionResult> Stats()
    {
        var s = await _dbSvc.GetMasterStatsAsync();
        return Ok(new
        {
            total         = s.Total,
            objectCount   = s.ObjectCount,
            variableCount = s.VariableCount,
            maxLevel      = s.MaxLevel,
            dataTypes     = s.DataTypes
        });
    }

    /// <summary>node_map_master 필터 조회 (페이지네이션 포함, names OR 조건)</summary>
    [HttpGet("query")]
    public async Task<IActionResult> Query(
        [FromQuery] int?         minLevel,
        [FromQuery] int?         maxLevel,
        [FromQuery] string?      nodeClass,
        [FromQuery] List<string>? names,
        [FromQuery] string?      nodeId,
        [FromQuery] string?      dataType,
        [FromQuery] int          limit  = 100,
        [FromQuery] int          offset = 0)
    {
        var r = await _dbSvc.QueryMasterAsync(
            minLevel, maxLevel, nodeClass, names, nodeId, dataType, limit, offset);

        return Ok(new
        {
            total = r.Total,
            items = r.Items.Select(x => new
            {
                x.Id, x.Level, x.Class, x.Name, x.NodeId, x.DataType
            })
        });
    }
}

// ── 하이퍼테이블 관리 ───────────────────────────────────────────────────────────

[ApiController]
[Route("api/experion/hypertable")]
public class ExperionHypertableController : ControllerBase
{
    private readonly IExperionDbService _dbSvc;

    public ExperionHypertableController(IExperionDbService dbSvc) => _dbSvc = dbSvc;

    /// <summary>하이퍼테이블 상태 조회</summary>
    [HttpGet("status")]
    public async Task<IActionResult> GetStatus()
    {
        var status = await _dbSvc.GetHypertableStatusAsync();
        return Ok(new
        {
            isHypertable           = status.IsHypertable,
            tableName              = status.TableName,
            statusMessage          = status.StatusMessage,
            recordCount            = status.RecordCount,
            hasRetentionPolicy     = status.HasRetentionPolicy,
            hasCompression         = status.HasCompression,
            hasContinuousAggregate = status.HasContinuousAggregate
        });
    }

    /// <summary>하이퍼테이블 수동 생성</summary>
    [HttpPost("create")]
    public async Task<IActionResult> Create([FromBody] HypertableCreateDto request)
    {
        var createRequest = new HypertableCreateRequest
        {
            TableName = request.TableName ?? "history_table",
            TimeColumn = request.TimeColumn ?? "recorded_at",
            TimeInterval = request.TimeInterval ?? "1 day",
            MigrateData = request.MigrateData,
            SetRetentionPolicy = request.SetRetentionPolicy,
            RetentionPeriod = request.RetentionPeriod ?? "90 days",
            EnableCompression = request.EnableCompression,
            CompressionPeriod = request.CompressionPeriod ?? "1 day",
            CreateContinuousAggregate = request.CreateContinuousAggregate
        };

        var result = await _dbSvc.CreateHypertableAsync(createRequest);

        return result.Success
            ? Ok(new { result.Success, result.Message, result.TableName })
            : (IActionResult)(result.TableName != null
                ? StatusCode(409, new { result.Success, result.Message })
                : StatusCode(500, new { result.Success, result.Message }));
    }
}
