using Hc900Crawler.Core.Application.DTOs;
using Hc900Crawler.Core.Application.Interfaces;
using Hc900Crawler.Core.Domain.Entities;
using Hc900Crawler.Infrastructure.Database;
using Microsoft.EntityFrameworkCore;

namespace Hc900Crawler.Core.Application.Services;

public class TagMappingService : ITagMappingService
{
    private readonly Hc900DbContext _dbContext;
    
    public TagMappingService(Hc900DbContext dbContext)
    {
        _dbContext = dbContext;
    }
    
    public async Task<(int Total, IEnumerable<TagMappingResult> Items)> GetMappingsAsync(int page, int pageSize)
    {
        var query = from pe in _dbContext.PidEquipment
                    join rt in _dbContext.RealtimePoints
                        on pe.ExperionTagId equals rt.Id into joined
                    from rt in joined.DefaultIfEmpty()
                    select new TagMappingResult
                    {
                        PidEquipmentId = pe.Id,
                        TagNo = pe.TagNo,
                        EquipmentName = pe.EquipmentName,
                        InstrumentType = pe.InstrumentType,
                        LineNumber = pe.LineNumber,
                        PidDrawingNo = pe.PidDrawingNo,
                        Confidence = pe.Confidence,
                        IsActive = pe.IsActive,
                        ExperionTagId = pe.ExperionTagId,
                        ExperionTagName = rt == null ? null : rt.TagName,
                        ExperionNodeId = rt == null ? null : rt.NodeId
                    };
        
        var total = await query.CountAsync();
        var items = await query
            .OrderByDescending(e => e.Confidence)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();
        
        return (total, items);
    }
    
    public async Task<TagMappingResult?> GetMappingByIdAsync(long id)
    {
        var item = await _dbContext.PidEquipment
            .Include(e => e.ExperionTag)
            .FirstOrDefaultAsync(e => e.Id == id);
        
        if (item == null) return null;
        
        return new TagMappingResult
        {
            PidEquipmentId = item.Id,
            TagNo = item.TagNo,
            EquipmentName = item.EquipmentName,
            InstrumentType = item.InstrumentType,
            LineNumber = item.LineNumber,
            PidDrawingNo = item.PidDrawingNo,
            Confidence = item.Confidence,
            IsActive = item.IsActive,
            ExperionTagId = item.ExperionTagId,
            ExperionTagName = item.ExperionTag?.TagName,
            ExperionNodeId = item.ExperionTag?.NodeId
        };
    }
    
    public async Task<TagMappingResult> CreateMappingAsync(CreateMappingRequest request)
    {
        var equipment = await _dbContext.PidEquipment.FindAsync(request.PidEquipmentId);
        if (equipment == null) throw new InvalidOperationException("P&ID 장비를 찾을 수 없습니다.");
        
        var tag = await _dbContext.RealtimePoints.FindAsync(request.ExperionTagId);
        if (tag == null) throw new InvalidOperationException("실시간 태그를 찾을 수 없습니다.");
        
        equipment.ExperionTagId = tag.Id;
        equipment.UpdatedAt = DateTime.UtcNow;
        await _dbContext.SaveChangesAsync();
        
        return new TagMappingResult
        {
            PidEquipmentId = equipment.Id,
            TagNo = equipment.TagNo,
            EquipmentName = equipment.EquipmentName,
            InstrumentType = equipment.InstrumentType,
            LineNumber = equipment.LineNumber,
            PidDrawingNo = equipment.PidDrawingNo,
            Confidence = equipment.Confidence,
            IsActive = equipment.IsActive,
            ExperionTagId = equipment.ExperionTagId,
            ExperionTagName = tag.TagName,
            ExperionNodeId = tag.NodeId
        };
    }
    
    public async Task UpdateMappingAsync(long id, UpdateMappingRequest request)
    {
        var equipment = await _dbContext.PidEquipment.FindAsync(id);
        if (equipment == null) throw new InvalidOperationException("P&ID 장비를 찾을 수 없습니다.");
        
        if (request.ExperionTagId.HasValue)
        {
            var tag = await _dbContext.RealtimePoints.FindAsync(request.ExperionTagId.Value);
            if (tag == null) throw new InvalidOperationException("실시간 태그를 찾을 수 없습니다.");
            
            equipment.ExperionTagId = tag.Id;
        }
        
        if (request.IsActive.HasValue)
            equipment.IsActive = request.IsActive.Value;
        
        equipment.UpdatedAt = DateTime.UtcNow;
        await _dbContext.SaveChangesAsync();
    }
    
    public async Task ClearMappingAsync(long id)
    {
        var equipment = await _dbContext.PidEquipment.FindAsync(id);
        if (equipment == null) throw new InvalidOperationException("P&ID 장비를 찾을 수 없습니다.");
        
        equipment.ExperionTagId = null;
        equipment.UpdatedAt = DateTime.UtcNow;
        await _dbContext.SaveChangesAsync();
    }
    
    public async Task<int> GetUnmappedCountAsync()
        => await _dbContext.PidEquipment.CountAsync(e => e.ExperionTagId == null);
    
    public async Task<int> GetMappedCountAsync()
        => await _dbContext.PidEquipment.CountAsync(e => e.ExperionTagId != null);
    
    public async Task<IEnumerable<string>> GetAvailableTagsAsync()
    {
        var mappedTagIds = await _dbContext.PidEquipment
            .Where(e => e.ExperionTagId != null)
            .Select(e => e.ExperionTagId)
            .ToListAsync();
        
        return await _dbContext.RealtimePoints
            .Where(t => !mappedTagIds.Contains(t.Id))
            .Select(t => t.TagName)
            .OrderBy(t => t)
            .ToListAsync();
    }
}
