using Hc900Crawler.Core.Application.Interfaces;
using Hc900Crawler.Core.Domain.Entities;
using Hc900Crawler.Infrastructure.Database;
using Hc900Crawler.Infrastructure.Hc900;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace Hc900Crawler.Web.Controllers;

// ── Gateway 상태 / 제어 ──────────────────────────────────────────────────────

[ApiController]
[Route("api/gateway")]
public class GatewayController : ControllerBase
{
    private readonly ControllerGrpcClientPool _clientPool;
    private readonly Hc900WriteService _write;
    private readonly Hc900RealtimeService _realtime;

    public GatewayController(ControllerGrpcClientPool clientPool, Hc900WriteService write, Hc900RealtimeService realtime)
    {
        _clientPool = clientPool;
        _write = write;
        _realtime = realtime;
    }

    /// <summary>gRPC Gateway 헬스체크</summary>
    [HttpGet("health")]
    public async Task<IActionResult> Health([FromQuery] string? controllerId = null)
    {
        try
        {
            var id = controllerId ?? _clientPool.EnabledControllerIds.FirstOrDefault() ?? "HC1";
            var client = _clientPool.GetClient(id);
            var h = await client.HealthCheckAsync(new Hc900.Gateway.HealthCheckRequest());
            return Ok(new
            {
                controllerId = id,
                status = h.Status.ToString(),
                controllerIp = h.ControllerIp,
                activeTags = h.ActiveTags,
                pollCount = h.PollCount,
                lastPollMs = h.LastPollMs,
                uptimeSec = h.UptimeSec
            });
        }
        catch (Exception ex)
        {
            return Ok(new { status = "NOT_SERVING", error = ex.Message });
        }
    }

    /// <summary>실시간 수집 서비스 상태</summary>
    [HttpGet("status")]
    public IActionResult Status() => Ok(new
    {
        isConnected = _realtime.IsConnected,
        pollCount   = _realtime.PollCount,
        lastPollAt  = _realtime.LastPollAt,
        controllers = _realtime.ControllerConnected,
    });

    /// <summary>레지스터 태그 목록 조회</summary>
    [HttpGet("tags")]
    public async Task<IActionResult> ListTags([FromQuery] string? controllerId = null, [FromQuery] string filter = "", [FromQuery] int limit = 100)
    {
        var id = controllerId ?? _clientPool.EnabledControllerIds.FirstOrDefault() ?? "HC1";
        var client = _clientPool.GetClient(id);
        var resp = await client.ListTagsAsync(new Hc900.Gateway.ListTagsRequest { Filter = filter, Limit = limit });
        return Ok(resp.Tags.Select(t => new
        {
            tagName = t.TagName,
            address = t.Address,
            count = t.Count,
            type = t.Type,
            access = t.Access,
            description = t.Description,
            eu = t.Eu
        }));
    }

    /// <summary>태그 쓰기</summary>
    [HttpPost("write")]
    public async Task<IActionResult> Write([FromBody] WriteTagDto dto)
    {
        var (success, error) = await _write.WriteTagAsync(dto.ControllerId, dto.TagName, dto.Value);
        return Ok(new { success, error });
    }
}

public class WriteTagDto
{
    public string ControllerId { get; set; } = "HC1";
    public string TagName      { get; set; } = "";
    public double Value        { get; set; }
}

// ── Realtime Points ───────────────────────────────────────────────────────────

[ApiController]
[Route("api/realtime")]
public class RealtimeController : ControllerBase
{
    private readonly IExperionDbService _db;

    public RealtimeController(IExperionDbService db) => _db = db;

    /// <summary>전체 realtime 포인트 조회 (선택적 controllerId 필터)</summary>
    [HttpGet("points")]
    public async Task<IActionResult> GetPoints([FromQuery] string? controllerId = null)
    {
        var points = await _db.GetRealtimePointsAsync();
        if (!string.IsNullOrEmpty(controllerId))
            points = points.Where(p => p.ControllerId == controllerId);
        return Ok(points);
    }

    [HttpDelete("points/{id}")]
    public async Task<IActionResult> DeletePoint(int id, [FromQuery] bool purgeHistory = false)
    {
        var result = await _db.DeleteRealtimePointAsync(id, purgeHistory);
        return Ok(result);
    }
}

// ── History ───────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/history")]
public class HistoryController : ControllerBase
{
    private readonly IExperionDbService _db;

    public HistoryController(IExperionDbService db) => _db = db;

    [HttpPost("query")]
    public async Task<IActionResult> Query([FromBody] HistoryQueryDto dto)
    {
        var result = await _db.QueryHistoryAsync(dto.TagNames, dto.From, dto.To, dto.Limit);
        return Ok(result);
    }

    [HttpPost("query-interval")]
    public async Task<IActionResult> QueryInterval([FromBody] Hc900Crawler.Core.Application.Interfaces.HistoryIntervalQueryRequest req)
    {
        var result = await _db.QueryHistoryWithIntervalAsync(req);
        return Ok(result);
    }

    [HttpGet("tags")]
    public async Task<IActionResult> GetTagNames()
    {
        var tags = await _db.GetTagNamesAsync();
        return Ok(tags);
    }
}

public class HistoryQueryDto
{
    public string? ControllerId { get; set; }
    public List<string> TagNames { get; set; } = new();
    public DateTime? From { get; set; }
    public DateTime? To { get; set; }
    public int Limit { get; set; } = 1000;
}

// ── Tag Metadata ──────────────────────────────────────────────────────────────

[ApiController]
[Route("api/metadata")]
public class MetadataController : ControllerBase
{
    private readonly IExperionDbService _db;

    public MetadataController(IExperionDbService db) => _db = db;

    [HttpGet]
    public async Task<IActionResult> Get([FromQuery] string? baseTag = null)
    {
        using var scope = HttpContext.RequestServices.CreateScope();
        var ctx = scope.ServiceProvider.GetRequiredService<Hc900DbContext>();
        var q = ctx.TagMetadata.AsQueryable();
        if (!string.IsNullOrEmpty(baseTag))
            q = q.Where(x => x.BaseTag == baseTag);
        return Ok(await q.ToListAsync());
    }
}

// ── Event History ─────────────────────────────────────────────────────────────

[ApiController]
[Route("api/events")]
public class EventHistoryController : ControllerBase
{
    private readonly IExperionDbService _db;

    public EventHistoryController(IExperionDbService db) => _db = db;

    [HttpPost("query")]
    public async Task<IActionResult> Query([FromBody] EventQueryDto dto)
    {
        var result = await _db.QueryEventHistoryAsync(
            dto.TagName, dto.Area, dto.SubArea, dto.EventType,
            dto.From ?? DateTime.UtcNow.AddDays(-1),
            dto.To   ?? DateTime.UtcNow,
            dto.Limit);
        return Ok(result);
    }
}

public class EventQueryDto
{
    public string? ControllerId { get; set; }
    public string? TagName   { get; set; }
    public string? Area      { get; set; }
    public string? SubArea   { get; set; }
    public string? EventType { get; set; }
    public DateTime? From    { get; set; }
    public DateTime? To      { get; set; }
    public int Limit         { get; set; } = 500;
}

// ── HC900 Tag Manager ─────────────────────────────────────────────────────────

[ApiController]
[Route("api/hc900/tags")]
public class Hc900TagManagerController : ControllerBase
{
    private readonly Hc900DbContext _ctx;

    public Hc900TagManagerController(Hc900DbContext ctx) => _ctx = ctx;

    [HttpGet]
    public async Task<IActionResult> Get(
        [FromQuery] string? controllerId = null,
        [FromQuery] string? paramType = null,
        [FromQuery] int? loopNo = null,
        [FromQuery] bool? active = null,
        [FromQuery] string? search = null)
    {
        var q = _ctx.Hc900MapEntries.AsQueryable();

        if (!string.IsNullOrEmpty(controllerId)) q = q.Where(x => x.ControllerId == controllerId);
        if (active.HasValue)        q = q.Where(x => x.IsActive == active.Value);
        if (!string.IsNullOrEmpty(paramType)) q = q.Where(x => x.ParamType == paramType);
        if (loopNo.HasValue)        q = q.Where(x => x.LoopNo == loopNo.Value);
        if (!string.IsNullOrEmpty(search))
            q = q.Where(x => x.TagName.Contains(search) || x.Hc900Tag.Contains(search));

        var entries = await q.ToListAsync();
        entries.Sort((a, b) =>
        {
            var ca = char.ToUpper(a.TagName[0]);
            var cb = char.ToUpper(b.TagName[0]);
            if (ca != cb) return ca.CompareTo(cb);

            var dotA = a.TagName.LastIndexOf('.');
            var dotB = b.TagName.LastIndexOf('.');
            var baseA = dotA >= 0 ? a.TagName[..dotA] : a.TagName;
            var baseB = dotB >= 0 ? b.TagName[..dotB] : b.TagName;
            var propA = dotA >= 0 ? a.TagName[(dotA + 1)..] : "";
            var propB = dotB >= 0 ? b.TagName[(dotB + 1)..] : "";

            var cmp = string.Compare(baseA, baseB, StringComparison.OrdinalIgnoreCase);
            if (cmp != 0) return cmp;
            return string.Compare(propA, propB, StringComparison.OrdinalIgnoreCase);
        });

        var tagNames = entries.Select(e => e.TagName).ToList();
        var liveValues = await _ctx.RealtimePoints
            .Where(r => tagNames.Contains(r.TagName))
            .ToDictionaryAsync(r => r.TagName, r => new { r.LiveValue, r.Timestamp });

        var result = entries.Select(e =>
        {
            liveValues.TryGetValue(e.TagName, out var live);
            return new
            {
                e.Id, e.TagName, e.Hc900Tag, e.ControllerId,
                ModbusAddrHex = $"0x{e.ModbusAddr:X4}",
                e.ModbusAddr, e.DataType, e.Access,
                e.LoopNo, e.ParamType, e.IsActive,
                LiveValue = live?.LiveValue,
                Timestamp = live?.Timestamp,
            };
        });

        return Ok(result);
    }

    [HttpPut("{id}/active")]
    public async Task<IActionResult> SetActive(int id, [FromBody] bool active)
    {
        var entry = await _ctx.Hc900MapEntries.FindAsync(id);
        if (entry == null) return NotFound();
        entry.IsActive = active;
        await _ctx.SaveChangesAsync();
        return Ok(new { entry.Id, entry.TagName, entry.IsActive });
    }

    [HttpPost("bulk-active")]
    public async Task<IActionResult> BulkSetActive([FromBody] BulkActiveDto dto)
    {
        var q = _ctx.Hc900MapEntries.AsQueryable();
        if (!string.IsNullOrEmpty(dto.ParamType)) q = q.Where(x => x.ParamType == dto.ParamType);
        if (dto.LoopNo.HasValue) q = q.Where(x => x.LoopNo == dto.LoopNo.Value);
        if (dto.Ids?.Any() == true) q = q.Where(x => dto.Ids.Contains(x.Id));

        var count = await q.ExecuteUpdateAsync(s => s.SetProperty(x => x.IsActive, dto.Active));
        return Ok(new { updated = count, active = dto.Active });
    }

    [HttpGet("param-types")]
    public async Task<IActionResult> GetParamTypes()
    {
        var types = await _ctx.Hc900MapEntries
            .Where(x => x.ParamType != null)
            .Select(x => x.ParamType!)
            .Distinct()
            .OrderBy(x => x)
            .ToListAsync();
        return Ok(types);
    }

    [HttpPut("{id}/controller")]
    public async Task<IActionResult> SetController(int id, [FromBody] string controllerId)
    {
        var entry = await _ctx.Hc900MapEntries.FindAsync(id);
        if (entry == null) return NotFound();
        entry.ControllerId = controllerId;
        await _ctx.SaveChangesAsync();
        return Ok(new { entry.Id, entry.ControllerId });
    }

    [HttpGet("controller-ids")]
    public async Task<IActionResult> GetControllerIds()
    {
        var ids = await _ctx.Hc900MapEntries
            .Select(x => x.ControllerId)
            .Distinct()
            .OrderBy(x => x)
            .ToListAsync();
        return Ok(ids);
    }

    [HttpGet("summary")]
    public async Task<IActionResult> GetSummary()
    {
        var total    = await _ctx.Hc900MapEntries.CountAsync();
        var active   = await _ctx.Hc900MapEntries.CountAsync(x => x.IsActive);
        var byType   = await _ctx.Hc900MapEntries
            .GroupBy(x => x.ParamType)
            .Select(g => new { paramType = g.Key, total = g.Count(), active = g.Count(x => x.IsActive) })
            .OrderBy(x => x.paramType)
            .ToListAsync();
        var liveCount = await _ctx.RealtimePoints.CountAsync();
        return Ok(new { total, active, inactive = total - active, liveCount, byType });
    }
}

public class BulkActiveDto
{
    public bool    Active     { get; set; }
    public string? ParamType  { get; set; }
    public int?    LoopNo     { get; set; }
    public List<int>? Ids     { get; set; }
}

// ── Sub-Area ──────────────────────────────────────────────────────────────────

[ApiController]
[Route("api/subarea")]
public class SubAreaController : ControllerBase
{
    private readonly IExperionDbService _db;

    public SubAreaController(IExperionDbService db) => _db = db;

    [HttpGet("{area}")]
    public async Task<IActionResult> Get(string area, [FromQuery] int page = 1, [FromQuery] int pageSize = 50)
    {
        var (tags, total) = await _db.GetSubAreaListByAreaAsync(area, page, pageSize);
        return Ok(new { total, tags });
    }

    [HttpPut("{baseTag}")]
    public async Task<IActionResult> Update(string baseTag, [FromBody] string? subArea)
    {
        var ok = await _db.UpdateSubAreaAsync(baseTag, subArea);
        return Ok(new { success = ok });
    }

    [HttpPost("seed")]
    public async Task<IActionResult> Seed([FromQuery] bool dryRun = true)
    {
        var result = await _db.SeedSubAreaAsync(dryRun);
        return Ok(result);
    }
}
