using line_gestao_api.Data; using line_gestao_api.Dtos; using line_gestao_api.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Globalization; using System.Text; namespace line_gestao_api.Controllers { [ApiController] [Route("api/[controller]")] [Authorize] public class TrocaNumeroController : ControllerBase { private readonly AppDbContext _db; public TrocaNumeroController(AppDbContext db) { _db = db; } // ========================================================== // ✅ GET LIST (PAGINADO) // ========================================================== [HttpGet] public async Task>> GetAll( [FromQuery] string? search, [FromQuery] int page = 1, [FromQuery] int pageSize = 20, [FromQuery] string? sortBy = "item", [FromQuery] string? sortDir = "asc") { page = page < 1 ? 1 : page; pageSize = pageSize < 1 ? 20 : pageSize; var q = _db.TrocaNumeroLines.AsNoTracking(); if (!string.IsNullOrWhiteSpace(search)) { var s = search.Trim(); var hasDateSearch = TryParseSearchDateUtcStart(s, out var searchDateStartUtc); var searchDateEndUtc = hasDateSearch ? searchDateStartUtc.AddDays(1) : DateTime.MinValue; q = q.Where(x => EF.Functions.ILike(x.LinhaAntiga ?? "", $"%{s}%") || EF.Functions.ILike(x.LinhaNova ?? "", $"%{s}%") || EF.Functions.ILike(x.ICCID ?? "", $"%{s}%") || EF.Functions.ILike(x.Motivo ?? "", $"%{s}%") || EF.Functions.ILike(x.Observacao ?? "", $"%{s}%") || EF.Functions.ILike(x.Item.ToString(), $"%{s}%") || (hasDateSearch && x.DataTroca != null && x.DataTroca.Value >= searchDateStartUtc && x.DataTroca.Value < searchDateEndUtc)); } var total = await q.CountAsync(); var sb = (sortBy ?? "item").Trim().ToLowerInvariant(); var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); q = sb switch { "item" => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item), "linhanova" => desc ? q.OrderByDescending(x => x.LinhaNova ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaNova ?? "").ThenBy(x => x.Item), "linhaantiga" => desc ? q.OrderByDescending(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.LinhaAntiga ?? "").ThenBy(x => x.Item), "iccid" => desc ? q.OrderByDescending(x => x.ICCID ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.ICCID ?? "").ThenBy(x => x.Item), "datatroca" => desc ? q.OrderByDescending(x => x.DataTroca).ThenBy(x => x.Item) : q.OrderBy(x => x.DataTroca).ThenBy(x => x.Item), "motivo" => desc ? q.OrderByDescending(x => x.Motivo ?? "").ThenBy(x => x.Item) : q.OrderBy(x => x.Motivo ?? "").ThenBy(x => x.Item), _ => desc ? q.OrderByDescending(x => x.Item) : q.OrderBy(x => x.Item) }; var items = await q .Skip((page - 1) * pageSize) .Take(pageSize) .Select(x => new TrocaNumeroListDto { Id = x.Id, Item = x.Item, LinhaAntiga = x.LinhaAntiga, LinhaNova = x.LinhaNova, ICCID = x.ICCID, DataTroca = x.DataTroca, Motivo = x.Motivo, Observacao = x.Observacao }) .ToListAsync(); return Ok(new PagedResult { Page = page, PageSize = pageSize, Total = total, Items = items }); } // ========================================================== // ✅ GET BY ID // ========================================================== [HttpGet("{id:guid}")] public async Task> GetById(Guid id) { var x = await _db.TrocaNumeroLines.AsNoTracking().FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); return Ok(ToDetailDto(x)); } // ========================================================== // ✅ CREATE // ========================================================== [HttpPost] [Authorize(Roles = "admin,gestor")] public async Task> Create([FromBody] CreateTrocaNumeroDto req) { var now = DateTime.UtcNow; var e = new TrocaNumeroLine { Id = Guid.NewGuid(), Item = req.Item ?? 0, LinhaAntiga = OnlyDigits(req.LinhaAntiga), LinhaNova = OnlyDigits(req.LinhaNova), ICCID = OnlyDigits(req.ICCID), DataTroca = ToUtc(req.DataTroca), Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim(), Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim(), CreatedAt = now, UpdatedAt = now }; _db.TrocaNumeroLines.Add(e); await _db.SaveChangesAsync(); return CreatedAtAction(nameof(GetById), new { id = e.Id }, ToDetailDto(e)); } // ========================================================== // ✅ UPDATE // ========================================================== [HttpPut("{id:guid}")] [Authorize(Roles = "admin,gestor")] public async Task Update(Guid id, [FromBody] UpdateTrocaNumeroRequest req) { var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); if (req.Item.HasValue) x.Item = req.Item.Value; x.LinhaAntiga = OnlyDigits(req.LinhaAntiga); x.LinhaNova = OnlyDigits(req.LinhaNova); x.ICCID = OnlyDigits(req.ICCID); x.DataTroca = ToUtc(req.DataTroca); x.Motivo = string.IsNullOrWhiteSpace(req.Motivo) ? null : req.Motivo.Trim(); x.Observacao = string.IsNullOrWhiteSpace(req.Observacao) ? null : req.Observacao.Trim(); x.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(); return NoContent(); } // ========================================================== // ✅ DELETE // ========================================================== [HttpDelete("{id:guid}")] [Authorize(Roles = "admin")] public async Task Delete(Guid id) { var x = await _db.TrocaNumeroLines.FirstOrDefaultAsync(a => a.Id == id); if (x == null) return NotFound(); _db.TrocaNumeroLines.Remove(x); await _db.SaveChangesAsync(); return NoContent(); } // ========================================================== // HELPERS // ========================================================== private static TrocaNumeroDetailDto ToDetailDto(TrocaNumeroLine x) => new() { Id = x.Id, Item = x.Item, LinhaAntiga = x.LinhaAntiga, LinhaNova = x.LinhaNova, ICCID = x.ICCID, DataTroca = x.DataTroca, Motivo = x.Motivo, Observacao = x.Observacao, CreatedAt = x.CreatedAt, UpdatedAt = x.UpdatedAt }; private static DateTime? ToUtc(DateTime? dt) { if (dt == null) return null; var v = dt.Value; return v.Kind == DateTimeKind.Utc ? v : (v.Kind == DateTimeKind.Local ? v.ToUniversalTime() : DateTime.SpecifyKind(v, DateTimeKind.Utc)); } private static string OnlyDigits(string? s) { if (string.IsNullOrWhiteSpace(s)) return ""; var sb = new StringBuilder(); foreach (var c in s) if (char.IsDigit(c)) sb.Append(c); return sb.ToString(); } private static bool TryParseSearchDateUtcStart(string value, out DateTime utcStart) { utcStart = default; if (string.IsNullOrWhiteSpace(value)) return false; var s = value.Trim(); DateTime parsed; if (DateTime.TryParse(s, new CultureInfo("pt-BR"), DateTimeStyles.None, out parsed) || DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None, out parsed)) { utcStart = DateTime.SpecifyKind(parsed.Date, DateTimeKind.Utc); return true; } return false; } } }