using System.Text;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Localization;

namespace SharepointToolbox.Services.Export;

/// <summary>
/// Exports VersionCleanupResult list to a self-contained sortable/filterable HTML report.
/// Summary header shows totals (files trimmed, versions deleted, bytes freed); a single
/// table lists every processed file with sort/filter controls. No external assets.
/// </summary>
public class VersionCleanupHtmlExportService
{
    public string BuildHtml(IReadOnlyList<VersionCleanupResult> results, ReportBranding? branding = null)
    {
        var T = TranslationSource.Instance;
        var sb = new StringBuilder();

        long totalBytes = results.Sum(r => r.BytesFreed);
        int totalDeleted = results.Sum(r => r.VersionsDeleted);
        int totalFiles = results.Count(r => r.VersionsDeleted > 0);

        sb.AppendLine("<!DOCTYPE html>");
        sb.AppendLine("<html lang=\"en\">");
        sb.AppendLine("<head>");
        sb.AppendLine("<meta charset=\"UTF-8\">");
        sb.AppendLine("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">");
        sb.AppendLine($"<title>{T["report.title.versions"]}</title>");
        sb.AppendLine("""
            <style>
              body { font-family: 'Segoe UI', Arial, sans-serif; font-size: 13px; margin: 20px; background: #f5f5f5; }
              h1 { color: #0078d4; }
              .summary { display: flex; gap: 24px; margin: 12px 0 16px 0; padding: 12px 16px;
                         background: #e8f1fb; border-radius: 6px; }
              .summary .item { display: flex; flex-direction: column; }
              .summary .label { font-size: 11px; color: #555; text-transform: uppercase; letter-spacing: .5px; }
              .summary .value { font-size: 18px; font-weight: 600; color: #0078d4; }
              .toolbar { margin-bottom: 10px; display: flex; gap: 12px; align-items: center; }
              .toolbar label { font-weight: 600; }
              #filterInput { padding: 6px 10px; border: 1px solid #ccc; border-radius: 4px; width: 280px; font-size: 13px; }
              #resultCount { font-size: 12px; color: #666; }
              table { border-collapse: collapse; width: 100%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.15); }
              th { background: #0078d4; color: #fff; padding: 8px 12px; text-align: left; cursor: pointer;
                   font-weight: 600; user-select: none; white-space: nowrap; }
              th:hover { background: #106ebe; }
              th.sorted-asc::after  { content: ' ▲'; font-size: 10px; }
              th.sorted-desc::after { content: ' ▼'; font-size: 10px; }
              td { padding: 6px 12px; border-bottom: 1px solid #e0e0e0; word-break: break-all; }
              tr:hover td { background: #f0f7ff; }
              tr.hidden { display: none; }
              tr.err td { background: #fff4f4; }
              .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
              .err-cell { color: #b00020; }
              .generated { font-size: 11px; color: #888; margin-top: 12px; }
            </style>
            </head>
            <body>
            """);
        sb.Append(BrandingHtmlHelper.BuildBrandingHeader(branding));
        sb.AppendLine($"<h1>{T["report.title.versions_short"]}</h1>");

        sb.AppendLine($"""
            <div class="summary">
              <div class="item"><span class="label">{T["versions.summary.files"]}</span><span class="value">{totalFiles:N0}</span></div>
              <div class="item"><span class="label">{T["versions.summary.deleted"]}</span><span class="value">{totalDeleted:N0}</span></div>
              <div class="item"><span class="label">{T["versions.summary.freed"]}</span><span class="value">{FormatSize(totalBytes)}</span></div>
            </div>
            <div class="toolbar">
              <label for="filterInput">{T["report.filter.label"]}</label>
              <input id="filterInput" type="text" placeholder="{T["report.filter.placeholder_rows"]}" oninput="filterTable()" />
              <span id="resultCount"></span>
            </div>
            """);

        sb.AppendLine($"""
            <table id="resultsTable">
              <thead>
                <tr>
                  <th onclick="sortTable(0)">{T["report.col.site"]}</th>
                  <th onclick="sortTable(1)">{T["versions.col.library"]}</th>
                  <th onclick="sortTable(2)">{T["versions.col.file"]}</th>
                  <th onclick="sortTable(3)">{T["versions.col.path"]}</th>
                  <th class="num" onclick="sortTable(4)">{T["versions.col.before"]}</th>
                  <th class="num" onclick="sortTable(5)">{T["versions.col.deleted"]}</th>
                  <th class="num" onclick="sortTable(6)">{T["versions.col.remaining"]}</th>
                  <th class="num" onclick="sortTable(7)">{T["versions.col.freed"]}</th>
                  <th onclick="sortTable(8)">{T["versions.col.error"]}</th>
                </tr>
              </thead>
              <tbody>
            """);

        foreach (var r in results)
        {
            string rowClass = string.IsNullOrEmpty(r.Error) ? string.Empty : " class=\"err\"";
            string errCell = string.IsNullOrEmpty(r.Error)
                ? string.Empty
                : $"<span class=\"err-cell\">{H(r.Error)}</span>";

            sb.AppendLine($"""
                  <tr{rowClass}>
                    <td>{H(r.SiteUrl)}</td>
                    <td>{H(r.Library)}</td>
                    <td>{H(r.FileName)}</td>
                    <td>{H(r.FileServerRelativeUrl)}</td>
                    <td class="num" data-sort="{r.VersionsBefore}">{r.VersionsBefore:N0}</td>
                    <td class="num" data-sort="{r.VersionsDeleted}">{r.VersionsDeleted:N0}</td>
                    <td class="num" data-sort="{r.VersionsRemaining}">{r.VersionsRemaining:N0}</td>
                    <td class="num" data-sort="{r.BytesFreed}">{FormatSize(r.BytesFreed)}</td>
                    <td>{errCell}</td>
                  </tr>
                """);
        }

        sb.AppendLine("  </tbody>\n</table>");

        int count = results.Count;
        sb.AppendLine($"<p class=\"generated\">{T["report.text.generated_colon"]} {DateTime.Now:yyyy-MM-dd HH:mm} — {count:N0} {T["report.text.results_parens"]}</p>");

        sb.AppendLine($$"""
            <script>
            var sortDir = {};
            function sortTable(col) {
              var tbl = document.getElementById('resultsTable');
              var tbody = tbl.tBodies[0];
              var rows = Array.from(tbody.rows);
              var asc = sortDir[col] !== 'asc';
              sortDir[col] = asc ? 'asc' : 'desc';
              rows.sort(function(a, b) {
                var av = a.cells[col].dataset.sort || a.cells[col].innerText;
                var bv = b.cells[col].dataset.sort || b.cells[col].innerText;
                var an = parseFloat(av), bn = parseFloat(bv);
                if (!isNaN(an) && !isNaN(bn)) return asc ? an - bn : bn - an;
                return asc ? av.localeCompare(bv) : bv.localeCompare(av);
              });
              rows.forEach(function(r) { tbody.appendChild(r); });
              var ths = tbl.tHead.rows[0].cells;
              for (var i = 0; i < ths.length; i++) {
                ths[i].className = (i === col) ? (asc ? 'sorted-asc' : 'sorted-desc') : '';
              }
            }
            function filterTable() {
              var q = document.getElementById('filterInput').value.toLowerCase();
              var rows = document.getElementById('resultsTable').tBodies[0].rows;
              var visible = 0;
              for (var i = 0; i < rows.length; i++) {
                var match = rows[i].innerText.toLowerCase().indexOf(q) >= 0;
                rows[i].className = (rows[i].className.indexOf('err') >= 0 ? 'err ' : '') + (match ? '' : 'hidden');
                if (match) visible++;
              }
              document.getElementById('resultCount').innerText = q ? (visible + ' {{T["report.text.of"]}} {{count:N0}} {{T["report.text.shown"]}}') : '';
            }
            window.onload = function() {
              document.getElementById('resultCount').innerText = '{{count:N0}} {{T["report.text.results_parens"]}}';
            };
            </script>
            </body></html>
            """);

        return sb.ToString();
    }

    /// <summary>Writes the HTML report to <paramref name="filePath"/> as UTF-8.</summary>
    public async Task WriteAsync(IReadOnlyList<VersionCleanupResult> results, string filePath, CancellationToken ct, ReportBranding? branding = null)
    {
        var html = BuildHtml(results, branding);
        await System.IO.File.WriteAllTextAsync(filePath, html, Encoding.UTF8, ct);
    }

    private static string H(string value) =>
        System.Net.WebUtility.HtmlEncode(value ?? string.Empty);

    private static string FormatSize(long bytes)
    {
        if (bytes >= 1_073_741_824L) return $"{bytes / 1_073_741_824.0:F2} GB";
        if (bytes >= 1_048_576L)     return $"{bytes / 1_048_576.0:F2} MB";
        if (bytes >= 1024L)          return $"{bytes / 1024.0:F2} KB";
        return $"{bytes} B";
    }
}
