using System.Text;
using System.Xml.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SIGCM.Domain.Entities;
using SIGCM.Domain.Interfaces;
using SIGCM.Infrastructure.Repositories;

namespace SIGCM.API.Controllers;

[ApiController]
[Route("api/[controller]")]
[Authorize(Roles = "Admin,Diagramador")]
public class ExportsController : ControllerBase
{
  private readonly IListingRepository _listingRepo;
  private readonly ICategoryRepository _categoryRepo;
  private readonly EditionClosureRepository _closureRepo;
  private readonly AuditRepository _auditRepo;

  public ExportsController(
    IListingRepository listingRepo, 
    ICategoryRepository categoryRepo,
    EditionClosureRepository closureRepo,
    AuditRepository auditRepo)
  {
    _listingRepo = listingRepo;
    _categoryRepo = categoryRepo;
    _closureRepo = closureRepo;
    _auditRepo = auditRepo;
  }

  /// <summary>
  /// Exportar XML para diagramación con estructura jerárquica y estilos
  /// Compatible con Adobe InDesign y QuarkXPress
  /// </summary>
  [HttpGet("diagram")]
  public async Task<IActionResult> DownloadDiagram([FromQuery] DateTime date)
  {
    // Verificar si la edición está cerrada
    var isClosed = await _closureRepo.IsEditionClosedAsync(date);
    
    // Obtener avisos para la fecha
    var listings = await _listingRepo.GetListingsForPrintAsync(date);
    
    if (!listings.Any())
    {
      return NotFound(new { message = "No hay avisos para exportar en esa fecha." });
    }

    // Obtener todas las categorías para armar la jerarquía
    var allCategories = await _categoryRepo.GetAllAsync();
    var categoryDict = allCategories.ToDictionary(c => c.Id, c => c);

    // Crear estructura jerárquica con categorías raíz
    var rootCategories = allCategories.Where(c => c.ParentId == null).OrderBy(c => c.Name);

    // Construir XML con jerarquía completa
    var doc = new XDocument(
      new XDeclaration("1.0", "utf-8", "yes"),
      new XElement("Edition",
        new XAttribute("date", date.ToString("yyyy-MM-dd")),
        new XAttribute("closed", isClosed.ToString().ToLower()),
        new XAttribute("totalAds", listings.Count()),
        
        // Recorrer categorías raíz
        from rootCat in rootCategories
        select BuildCategoryElement(rootCat, categoryDict, listings)
      )
    );

    // Registrar exportación en auditoría
    var userIdClaim = User.FindFirst("Id")?.Value;
    if (int.TryParse(userIdClaim, out int userId))
    {
      await _auditRepo.AddLogAsync(new AuditLog
      {
        UserId = userId,
        Action = "EXPORT_XML",
        EntityType = "Edition",
        Details = $"Exportación XML para {date:yyyy-MM-dd}. Avisos: {listings.Count()}",
        CreatedAt = DateTime.UtcNow
      });
    }

    // Convertir a bytes con formato indentado
    var xmlBytes = Encoding.UTF8.GetBytes(doc.ToString(SaveOptions.None));
    var fileName = $"Edicion_{date:yyyy-MM-dd}.xml";

    return File(xmlBytes, "application/xml", fileName);
  }

  // Método recursivo para construir la jerarquía de categorías
  private XElement BuildCategoryElement(
    Category category, 
    Dictionary<int, Category> categoryDict,
    IEnumerable<Listing> allListings)
  {
    // Filtrar avisos de esta categoría
    var adsInCategory = allListings
      .Where(l => l.CategoryId == category.Id)
      .OrderBy(l => l.Title) // Orden alfabético
      .ToList();

    // Crear elemento de categoría
    var categoryElement = new XElement("Category",
      new XAttribute("id", category.Id),
      new XAttribute("name", category.Name),
      new XAttribute("adCount", adsInCategory.Count)
    );

    // Agregar avisos si es una categoría hoja (que tiene avisos)
    if (adsInCategory.Any())
    {
      foreach (var ad in adsInCategory)
      {
        categoryElement.Add(
          new XElement("Ad",
            new XAttribute("id", ad.Id),
            
            // Atributos de estilo para InDesign
            new XAttribute("style", GetAdStyle(ad)),
            new XAttribute("bold", ad.IsBold.ToString().ToLower()),
            new XAttribute("frame", ad.IsFrame.ToString().ToLower()),
            new XAttribute("fontSize", ad.PrintFontSize),
            new XAttribute("alignment", ad.PrintAlignment),
            new XAttribute("days", ad.PrintDaysCount),
            
            // Texto del aviso en CDATA para proteger caracteres especiales
            new XCData(ad.PrintText ?? ad.Description ?? "")
          )
        );
      }
    }

    // Buscar sub-categorías recursivamente
    var subCategories = categoryDict.Values
      .Where(c => c.ParentId == category.Id)
      .OrderBy(c => c.Name);

    foreach (var subCat in subCategories)
    {
      categoryElement.Add(BuildCategoryElement(subCat, categoryDict, allListings));
    }

    return categoryElement;
  }

  // Generar string de estilo para InDesign
  private string GetAdStyle(Listing ad)
  {
    var styles = new List<string>();
    
    if (ad.IsBold) styles.Add("bold");
    if (ad.IsFrame) styles.Add("frame");
    if (ad.PrintFontSize != "normal") styles.Add($"size-{ad.PrintFontSize}");
    if (ad.PrintAlignment != "left") styles.Add($"align-{ad.PrintAlignment}");
    
    return styles.Any() ? string.Join(" ", styles) : "normal";
  }

  /// <summary>
  /// Obtener avisos huérfanos (pagados pero no incluidos en export)
  /// </summary>
  [HttpGet("orphan-ads")]
  public async Task<IActionResult> GetOrphanAds([FromQuery] DateTime date)
  {
    // Obtener avisos marcados para esa fecha pero con status incorrecto
    var allAds = await _listingRepo.GetListingsForPrintAsync(date);
    
    // Simulación: avisos con estado "Pending" o "Draft" que deberían haberse publicado
    var orphans = allAds.Where(a => 
      a.Status != "Published" && 
      a.AdFee > 0 && // Está pagado
      a.PrintStartDate.HasValue &&
      a.PrintStartDate.Value.Date <= date.Date
    );

    return Ok(new { 
      date = date.ToString("yyyy-MM-dd"),
      count = orphans.Count(),
      orphans = orphans.Select(a => new {
        a.Id,
        a.Title,
        a.Status,
        a.AdFee,
        a.PrintStartDate,
        CategoryName = a.CategoryName
      })
    });
  }

  /// <summary>
  /// Cerrar edición de una fecha específica
  /// Una vez cerrada, no se pueden modificar avisos de esa fecha
  /// </summary>
  [HttpPost("close-edition")]
  [Authorize(Roles = "Admin,Diagramador")]
  public async Task<IActionResult> CloseEdition([FromBody] CloseEditionRequest request)
  {
    // Verificar si ya está cerrada
    var alreadyClosed = await _closureRepo.IsEditionClosedAsync(request.EditionDate);
    if (alreadyClosed)
    {
      return BadRequest(new { message = "La edición ya está cerrada." });
    }

    // Obtener usuario actual
    var userIdClaim = User.FindFirst("Id")?.Value;
    if (!int.TryParse(userIdClaim, out int userId))
    {
      return Unauthorized();
    }

    // Crear cierre
    var closure = new EditionClosure
    {
      EditionDate = request.EditionDate.Date,
      ClosureDateTime = DateTime.UtcNow,
      ClosedByUserId = userId,
      IsClosed = true,
      Notes = request.Notes
    };

    var closureId = await _closureRepo.CloseEditionAsync(closure);

    // Auditoría
    await _auditRepo.AddLogAsync(new AuditLog
    {
      UserId = userId,
      Action = "CLOSE_EDITION",
      EntityId = closureId,
      EntityType = "EditionClosure",
      Details = $"Edición {request.EditionDate:yyyy-MM-dd} cerrada. {request.Notes}",
      CreatedAt = DateTime.UtcNow
    });

    return Ok(new { 
      message = "Edición cerrada exitosamente.",
      closureId,
      editionDate = request.EditionDate.ToString("yyyy-MM-dd")
    });
  }

  /// <summary>
  /// Reabrir una edición cerrada (solo Admin)
  /// </summary>
  [HttpPost("reopen-edition")]
  [Authorize(Roles = "Admin")]
  public async Task<IActionResult> ReopenEdition([FromQuery] DateTime date)
  {
    await _closureRepo.ReopenEditionAsync(date);

    // Auditoría
    var userIdClaim = User.FindFirst("Id")?.Value;
    if (int.TryParse(userIdClaim, out int userId))
    {
      await _auditRepo.AddLogAsync(new AuditLog
      {
        UserId = userId,
        Action = "REOPEN_EDITION",
        EntityType = "EditionClosure",
        Details = $"Edición {date:yyyy-MM-dd} reabierta",
        CreatedAt = DateTime.UtcNow
      });
    }

    return Ok(new { message = "Edición reabierta exitosamente." });
  }

  /// <summary>
  /// Obtener estado de una edición
  /// </summary>
  [HttpGet("edition-status")]
  public async Task<IActionResult> GetEditionStatus([FromQuery] DateTime date)
  {
    var closure = await _closureRepo.GetClosureByDateAsync(date);
    var listings = await _listingRepo.GetListingsForPrintAsync(date);

    return Ok(new {
      date = date.ToString("yyyy-MM-dd"),
      isClosed = closure?.IsClosed ?? false,
      closedAt = closure?.ClosureDateTime,
      closedBy = closure?.ClosedByUsername,
      adCount = listings.Count(),
      notes = closure?.Notes
    });
  }

  /// <summary>
  /// Historial de cierres
  /// </summary>
  [HttpGet("closure-history")]
  public async Task<IActionResult> GetClosureHistory()
  {
    var history = await _closureRepo.GetClosureHistoryAsync(30);
    return Ok(history);
  }
}

// DTO para cerrar edición
public class CloseEditionRequest
{
  public DateTime EditionDate { get; set; }
  public string? Notes { get; set; }
}