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

namespace SharepointToolbox.Services.Export;

/// <summary>
/// Exports permission entries to CSV format.
/// Ports PowerShell Merge-PermissionRows + Export-Csv functionality.
/// </summary>
public class CsvExportService
{
    private static string BuildHeader()
    {
        var T = TranslationSource.Instance;
        return $"\"{T["report.col.object"]}\",\"{T["report.col.title"]}\",\"{T["report.col.url"]}\",\"HasUniquePermissions\",\"Users\",\"UserLogins\",\"Type\",\"Permissions\",\"GrantedThrough\",\"TargetLabel\",\"TargetUrl\",\"SharingLinkType\"";
    }

    /// <summary>
    /// Builds a CSV string from the supplied permission entries.
    /// Merges rows with identical (Users, PermissionLevels, GrantedThrough) by pipe-joining URLs and Titles.
    /// </summary>
    public string BuildCsv(IReadOnlyList<PermissionEntry> entries)
    {
        var sb = new StringBuilder();
        sb.AppendLine(BuildHeader());

        // Merge: group by (Users, PermissionLevels, GrantedThrough) — port of PS Merge-PermissionRows
        var merged = entries
            .GroupBy(e => (e.Users, e.PermissionLevels, e.GrantedThrough))
            .Select(g => new
            {
                ObjectType    = g.First().ObjectType,
                Title         = string.Join(" | ", g.Select(e => e.Title).Distinct()),
                Url           = string.Join(" | ", g.Select(e => e.Url).Distinct()),
                HasUnique     = g.First().HasUniquePermissions,
                Users         = g.Key.Users,
                UserLogins    = g.First().UserLogins,
                PrincipalType = g.First().PrincipalType,
                Permissions   = g.Key.PermissionLevels,
                GrantedThrough = g.Key.GrantedThrough,
                TargetLabel   = g.First().TargetLabel ?? string.Empty,
                TargetUrl     = g.First().TargetUrl ?? string.Empty,
                SharingLinkType = g.First().SharingLinkType ?? string.Empty
            });

        foreach (var row in merged)
            sb.AppendLine(string.Join(",", new[]
            {
                Csv(row.ObjectType), Csv(row.Title), Csv(row.Url),
                Csv(row.HasUnique.ToString()), Csv(row.Users), Csv(row.UserLogins),
                Csv(row.PrincipalType), Csv(row.Permissions), Csv(row.GrantedThrough),
                Csv(row.TargetLabel), Csv(row.TargetUrl), Csv(row.SharingLinkType)
            }));

        return sb.ToString();
    }

    /// <summary>
    /// Writes the CSV to the specified file path using UTF-8 with BOM (for Excel compatibility).
    /// </summary>
    public async Task WriteAsync(IReadOnlyList<PermissionEntry> entries, string filePath, CancellationToken ct)
    {
        var csv = BuildCsv(entries);
        await ExportFileWriter.WriteCsvAsync(filePath, csv, ct);
    }

    /// <summary>
    /// Header for simplified CSV export — includes "SimplifiedLabels" and "RiskLevel" columns.
    /// </summary>
    private static string BuildSimplifiedHeader()
    {
        var T = TranslationSource.Instance;
        return $"\"{T["report.col.object"]}\",\"{T["report.col.title"]}\",\"{T["report.col.url"]}\",\"HasUniquePermissions\",\"Users\",\"UserLogins\",\"Type\",\"Permissions\",\"SimplifiedLabels\",\"RiskLevel\",\"GrantedThrough\",\"TargetLabel\",\"TargetUrl\",\"SharingLinkType\"";
    }

    /// <summary>
    /// Builds a CSV string from simplified permission entries.
    /// Includes SimplifiedLabels and RiskLevel columns after raw Permissions.
    /// Uses the same merge logic as the standard BuildCsv.
    /// </summary>
    public string BuildCsv(IReadOnlyList<SimplifiedPermissionEntry> entries)
    {
        var sb = new StringBuilder();
        sb.AppendLine(BuildSimplifiedHeader());

        var merged = entries
            .GroupBy(e => (e.Users, e.PermissionLevels, e.GrantedThrough))
            .Select(g => new
            {
                ObjectType     = g.First().ObjectType,
                Title          = string.Join(" | ", g.Select(e => e.Title).Distinct()),
                Url            = string.Join(" | ", g.Select(e => e.Url).Distinct()),
                HasUnique      = g.First().HasUniquePermissions,
                Users          = g.Key.Users,
                UserLogins     = g.First().UserLogins,
                PrincipalType  = g.First().PrincipalType,
                Permissions    = g.Key.PermissionLevels,
                SimplifiedLabels = g.First().SimplifiedLabels,
                RiskLevel      = g.First().RiskLevel.ToString(),
                GrantedThrough = g.Key.GrantedThrough,
                TargetLabel    = g.First().TargetLabel ?? string.Empty,
                TargetUrl      = g.First().TargetUrl ?? string.Empty,
                SharingLinkType = g.First().SharingLinkType ?? string.Empty
            });

        foreach (var row in merged)
            sb.AppendLine(string.Join(",", new[]
            {
                Csv(row.ObjectType), Csv(row.Title), Csv(row.Url),
                Csv(row.HasUnique.ToString()), Csv(row.Users), Csv(row.UserLogins),
                Csv(row.PrincipalType), Csv(row.Permissions), Csv(row.SimplifiedLabels),
                Csv(row.RiskLevel), Csv(row.GrantedThrough),
                Csv(row.TargetLabel), Csv(row.TargetUrl), Csv(row.SharingLinkType)
            }));

        return sb.ToString();
    }

    /// <summary>
    /// Writes simplified CSV to the specified file path.
    /// </summary>
    public async Task WriteAsync(IReadOnlyList<SimplifiedPermissionEntry> entries, string filePath, CancellationToken ct)
    {
        var csv = BuildCsv(entries);
        await ExportFileWriter.WriteCsvAsync(filePath, csv, ct);
    }

    /// <summary>
    /// Writes permission entries with optional per-site partitioning.
    /// Single → writes one file at <paramref name="basePath"/>.
    /// BySite → one file per site-collection URL, suffixed on the base path.
    /// </summary>
    public Task WriteAsync(
        IReadOnlyList<PermissionEntry> entries,
        string basePath,
        ReportSplitMode splitMode,
        CancellationToken ct)
        => ReportSplitHelper.WritePartitionedAsync(
            entries, basePath, splitMode,
            PartitionBySite,
            (part, path, c) => WriteAsync(part, path, c),
            ct);

    /// <summary>Simplified-entry split variant.</summary>
    public Task WriteAsync(
        IReadOnlyList<SimplifiedPermissionEntry> entries,
        string basePath,
        ReportSplitMode splitMode,
        CancellationToken ct)
        => ReportSplitHelper.WritePartitionedAsync(
            entries, basePath, splitMode,
            PartitionBySite,
            (part, path, c) => WriteAsync(part, path, c),
            ct);

    internal static IEnumerable<(string Label, IReadOnlyList<PermissionEntry> Partition)> PartitionBySite(
        IReadOnlyList<PermissionEntry> entries)
    {
        return entries
            .GroupBy(e => ReportSplitHelper.DeriveSiteCollectionUrl(e.Url), StringComparer.OrdinalIgnoreCase)
            .Select(g => (
                Label: ReportSplitHelper.DeriveSiteLabel(g.Key),
                Partition: (IReadOnlyList<PermissionEntry>)g.ToList()));
    }

    internal static IEnumerable<(string Label, IReadOnlyList<SimplifiedPermissionEntry> Partition)> PartitionBySite(
        IReadOnlyList<SimplifiedPermissionEntry> entries)
    {
        return entries
            .GroupBy(e => ReportSplitHelper.DeriveSiteCollectionUrl(e.Url), StringComparer.OrdinalIgnoreCase)
            .Select(g => (
                Label: ReportSplitHelper.DeriveSiteLabel(g.Key),
                Partition: (IReadOnlyList<SimplifiedPermissionEntry>)g.ToList()));
    }

    /// <summary>RFC 4180 CSV field escaping with formula-injection guard.</summary>
    private static string Csv(string value) => CsvSanitizer.Escape(value);
}
