namespace Inspectron.Epson.TemplateEngine.Rendering;

public class LayoutEngine
{
    public string AlignText(string text, string alignment, int lineWidth)
    {
        if (string.IsNullOrEmpty(text))
            return text ?? "";

        if (text.Length >= lineWidth)
            return text;

        return alignment.ToLowerInvariant() switch
        {
            "center" => CenterText(text, lineWidth),
            "right" => text.PadLeft(lineWidth),
            _ => text // left-aligned is default (no padding)
        };
    }

    private static string CenterText(string text, int lineWidth)
    {
        int totalPadding = lineWidth - text.Length;
        int leftPadding = totalPadding / 2;
        return new string(' ', leftPadding) + text;
    }

    public string FormatTwoColumns(string left, string right, int lineWidth)
    {
        left ??= "";
        right ??= "";

        int halfWidth = lineWidth / 2;
        return left.PadRight(halfWidth) + right.PadLeft(lineWidth - halfWidth);
    }

    public List<string> FormatTwoColumnsWithWrap(string left, string right, int lineWidth)
    {
        left ??= "";
        right ??= "";

        int halfWidth = lineWidth / 2;

        // Try simple format first
        if (left.Length + right.Length <= lineWidth)
        {
            int spaces = lineWidth - left.Length - right.Length;
            if (spaces < 1) spaces = 1;
            return new List<string> { left + new string(' ', spaces) + right };
        }

        // Wrap left side, right-align right on first line
        var leftLines = WrapText(left, halfWidth);
        var result = new List<string>();

        for (int i = 0; i < leftLines.Count; i++)
        {
            if (i == 0)
            {
                int spaces = lineWidth - leftLines[i].Length - right.Length;
                if (spaces < 1)
                {
                    result.Add(leftLines[i]);
                    result.Add(right.PadLeft(lineWidth));
                }
                else
                {
                    result.Add(leftLines[i] + new string(' ', spaces) + right);
                }
            }
            else
            {
                result.Add(leftLines[i]);
            }
        }

        return result;
    }

    public string FormatMultiColumn(List<(string text, int width, string align)> columns, int lineWidth)
    {
        var parts = new List<string>();

        // Calculate widths: distribute remaining width among columns without explicit width
        int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
        int unspecifiedCount = columns.Count(c => c.width <= 0);
        int remaining = lineWidth - totalExplicit;
        int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;

        foreach (var (text, width, align) in columns)
        {
            int colWidth = width > 0 ? width : defaultWidth;
            if (colWidth <= 0) colWidth = 1;

            string formatted = align.ToLowerInvariant() switch
            {
                "center" => CenterInWidth(text ?? "", colWidth),
                "right" => (text ?? "").PadLeft(colWidth),
                _ => (text ?? "").PadRight(colWidth)
            };

            // Truncate if too long
            if (formatted.Length > colWidth)
                formatted = formatted[..colWidth];

            parts.Add(formatted);
        }

        return string.Concat(parts);
    }

    public List<string> FormatMultiColumnWithWrap(
        List<(string text, int width, string align, bool wrap)> columns, int lineWidth)
    {
        // Calculate widths
        int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
        int unspecifiedCount = columns.Count(c => c.width <= 0);
        int remaining = lineWidth - totalExplicit;
        int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;

        var colWidths = columns.Select(c => c.width > 0 ? c.width : Math.Max(defaultWidth, 1)).ToList();

        // Wrap or truncate each column's text into lines
        var allColLines = new List<List<string>>();
        for (int i = 0; i < columns.Count; i++)
        {
            var (text, _, _, wrap) = columns[i];
            text ??= "";
            int w = colWidths[i];

            if (wrap)
                allColLines.Add(WrapText(text, w));
            else
                allColLines.Add(new List<string> { text.Length > w ? text[..w] : text });
        }

        // Find the tallest column
        int maxLines = allColLines.Max(l => l.Count);

        // Build each output line
        var result = new List<string>();
        for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
        {
            var parts = new List<string>();
            for (int colIdx = 0; colIdx < columns.Count; colIdx++)
            {
                string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
                int w = colWidths[colIdx];
                string formatted = columns[colIdx].align.ToLowerInvariant() switch
                {
                    "center" => CenterInWidth(cellText, w),
                    "right" => cellText.PadLeft(w),
                    _ => cellText.PadRight(w)
                };
                if (formatted.Length > w)
                    formatted = formatted[..w];
                parts.Add(formatted);
            }
            result.Add(string.Concat(parts));
        }

        return result;
    }

    public List<string> WrapText(string text, int maxWidth, int indent = 0)
    {
        var lines = new List<string>();

        if (string.IsNullOrEmpty(text) || maxWidth <= 0)
        {
            lines.Add(text ?? "");
            return lines;
        }

        if (text.Length <= maxWidth)
        {
            lines.Add(text);
            return lines;
        }

        var words = text.Split(' ');
        string currentLine = "";
        bool isFirst = true;

        foreach (var word in words)
        {
            int available = isFirst ? maxWidth : maxWidth - indent;
            if (available <= 0) available = 1;

            if (currentLine.Length == 0)
            {
                currentLine = word;
            }
            else if (currentLine.Length + 1 + word.Length <= available)
            {
                currentLine += " " + word;
            }
            else
            {
                if (isFirst)
                {
                    lines.Add(currentLine);
                    isFirst = false;
                }
                else
                {
                    lines.Add(new string(' ', indent) + currentLine);
                }
                currentLine = word;
            }
        }

        if (currentLine.Length > 0)
        {
            if (isFirst)
                lines.Add(currentLine);
            else
                lines.Add(new string(' ', indent) + currentLine);
        }

        return lines;
    }

    public string CreateSeparator(char character, int lineWidth)
    {
        return new string(character, lineWidth);
    }

    public List<string> FormatTable(
        List<(string text, int width, string align)> columns,
        List<List<string>> headerRows,
        List<List<string>> dataRows,
        int lineWidth,
        List<bool>? wrapFlags = null)
    {
        var result = new List<string>();

        // Calculate column widths
        var colWidths = CalculateTableColumnWidths(columns, lineWidth);

        bool hasWrap = wrapFlags != null && wrapFlags.Any(w => w);

        // Top border
        result.Add(FormatTableBorder(colWidths));

        // Header rows
        foreach (var row in headerRows)
        {
            if (hasWrap)
                result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
            else
                result.Add(FormatTableRow(row, colWidths, columns));
        }

        // Separator between header and data
        if (headerRows.Count > 0 && dataRows.Count > 0)
        {
            result.Add(FormatTableBorder(colWidths));
        }

        // Data rows
        foreach (var row in dataRows)
        {
            if (hasWrap)
                result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
            else
                result.Add(FormatTableRow(row, colWidths, columns));
        }

        // Bottom border
        result.Add(FormatTableBorder(colWidths));

        return result;
    }

    private List<int> CalculateTableColumnWidths(List<(string text, int width, string align)> columns, int lineWidth)
    {
        // Account for borders: |col1|col2|col3| = columns.Count + 1 border chars
        int availableWidth = lineWidth - columns.Count - 1;
        int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
        int unspecifiedCount = columns.Count(c => c.width <= 0);
        int remaining = availableWidth - totalExplicit;
        int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;

        var widths = new List<int>();
        foreach (var (_, width, _) in columns)
        {
            widths.Add(width > 0 ? width : Math.Max(defaultWidth, 1));
        }
        return widths;
    }

    private string FormatTableBorder(List<int> colWidths)
    {
        return "+" + string.Join("+", colWidths.Select(w => new string('-', w))) + "+";
    }

    private string FormatTableRow(List<string> cells, List<int> colWidths, List<(string text, int width, string align)> columns)
    {
        var parts = new List<string>();
        for (int i = 0; i < colWidths.Count; i++)
        {
            string cell = i < cells.Count ? cells[i] : "";
            string align = i < columns.Count ? columns[i].align : "left";
            parts.Add(FormatCellContent(cell, colWidths[i], align));
        }
        return "|" + string.Join("|", parts) + "|";
    }

    private List<string> FormatTableRows(List<string> cells, List<int> colWidths,
        List<(string text, int width, string align)> columns, List<bool> wrapFlags)
    {
        // Wrap each cell that has wrap enabled
        var allColLines = new List<List<string>>();
        for (int i = 0; i < colWidths.Count; i++)
        {
            string cell = i < cells.Count ? cells[i] : "";
            bool wrap = i < wrapFlags.Count && wrapFlags[i];

            if (wrap)
                allColLines.Add(WrapText(cell, colWidths[i]));
            else
                allColLines.Add(new List<string> { cell.Length > colWidths[i] ? cell[..colWidths[i]] : cell });
        }

        int maxLines = allColLines.Max(l => l.Count);

        var result = new List<string>();
        for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
        {
            var parts = new List<string>();
            for (int colIdx = 0; colIdx < colWidths.Count; colIdx++)
            {
                string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
                string align = colIdx < columns.Count ? columns[colIdx].align : "left";
                parts.Add(FormatCellContent(cellText, colWidths[colIdx], align));
            }
            result.Add("|" + string.Join("|", parts) + "|");
        }

        return result;
    }

    private string FormatCellContent(string text, int width, string align)
    {
        if (text.Length > width)
            text = text[..width];

        return align.ToLowerInvariant() switch
        {
            "center" => CenterInWidth(text, width),
            "right" => text.PadLeft(width),
            _ => text.PadRight(width)
        };
    }

    private static string CenterInWidth(string text, int width)
    {
        if (string.IsNullOrEmpty(text))
            return new string(' ', width);

        if (text.Length >= width)
            return text[..width];

        int totalPadding = width - text.Length;
        int leftPadding = totalPadding / 2;
        int rightPadding = totalPadding - leftPadding;

        return new string(' ', leftPadding) + text + new string(' ', rightPadding);
    }
}
