using System.Collections.Generic;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Services.Export;

namespace SharepointToolbox.Tests.Services.Export;

/// <summary>
/// Unit tests for UserAccessHtmlExportService (Phase 7 Plan 08).
/// Verifies: DOCTYPE, stats cards, dual-view sections, access type badges,
/// filter script, toggle script, HTML entity encoding.
/// </summary>
public class UserAccessHtmlExportServiceTests
{
    // ── Helper factory ────────────────────────────────────────────────────────

    private static ReportBranding MakeBranding(bool msp = true, bool client = false)
    {
        var mspLogo    = msp    ? new LogoData { Base64 = "bXNw", MimeType = "image/png" }  : null;
        var clientLogo = client ? new LogoData { Base64 = "Y2xpZW50", MimeType = "image/jpeg" } : null;
        return new ReportBranding(mspLogo, clientLogo);
    }

    private static UserAccessEntry MakeEntry(
        string userDisplay = "Alice Smith",
        string userLogin = "alice@contoso.com",
        string siteUrl = "https://contoso.sharepoint.com",
        string siteTitle = "Contoso",
        string objectType = "List",
        string objectTitle = "Docs",
        string objectUrl = "https://contoso.sharepoint.com/Docs",
        string permLevel = "Read",
        AccessType accessType = AccessType.Direct,
        string grantedThrough = "Direct Permissions",
        bool isHighPrivilege = false,
        bool isExternal = false) =>
        new(userDisplay, userLogin, siteUrl, siteTitle, objectType, objectTitle, objectUrl,
            permLevel, accessType, grantedThrough, isHighPrivilege, isExternal);

    private static readonly UserAccessEntry DefaultEntry = MakeEntry();

    // ── Test 1: BuildHtml contains DOCTYPE ───────────────────────────────────

    [Fact]
    public void BuildHtml_contains_doctype()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry });

        Assert.StartsWith("<!DOCTYPE html>", html.TrimStart());
    }

    // ── Test 2: BuildHtml has stats cards ─────────────────────────────────────

    [Fact]
    public void BuildHtml_has_stats_cards()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry });

        Assert.Contains("Total Accesses", html);
        Assert.Contains("stat-card", html);
    }

    // ── Test 3: BuildHtml has both view sections ──────────────────────────────

    [Fact]
    public void BuildHtml_has_both_views()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry });

        // By-user view
        Assert.Contains("view-user", html);
        // By-site view
        Assert.Contains("view-site", html);
    }

    // ── Test 4: BuildHtml has access type badge CSS classes ───────────────────

    [Fact]
    public void BuildHtml_has_access_type_badges()
    {
        var entries = new List<UserAccessEntry>
        {
            MakeEntry(accessType: AccessType.Direct),
            MakeEntry(userLogin: "bob@contoso.com", accessType: AccessType.Group),
            MakeEntry(userLogin: "carol@contoso.com", accessType: AccessType.Inherited)
        };

        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(entries);

        Assert.Contains("access-direct", html);
        Assert.Contains("access-group", html);
        Assert.Contains("access-inherited", html);
    }

    // ── Test 5: BuildHtml has filterTable JS function ─────────────────────────

    [Fact]
    public void BuildHtml_has_filter_script()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry });

        Assert.Contains("filterTable", html);
    }

    // ── Test 6: BuildHtml has toggleView JS function ──────────────────────────

    [Fact]
    public void BuildHtml_has_toggle_script()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry });

        Assert.Contains("toggleView", html);
    }

    // ── Test 7: BuildHtml encodes HTML entities ───────────────────────────────

    [Fact]
    public void BuildHtml_encodes_html_entities()
    {
        var entryWithScript = MakeEntry(objectTitle: "<script>alert('xss')</script>");
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { entryWithScript });

        // Raw script tag must not appear verbatim
        Assert.DoesNotContain("<script>alert", html);
        // Encoded form must be present
        Assert.Contains("&lt;script&gt;", html);
    }

    // ── Branding tests ────────────────────────────────────────────────────────

    [Fact]
    public void BuildHtml_WithBranding_ContainsLogoImg()
    {
        var svc = new UserAccessHtmlExportService();
        var html = svc.BuildHtml(new[] { DefaultEntry }, branding: MakeBranding(msp: true));
        Assert.Contains("data:image/png;base64,bXNw", html);
    }

    // ── Consolidation tests (RPT-03-b through RPT-03-e) ──────────────────────

    // Shared test data: 3 entries where 2 share the same consolidation key
    private static IReadOnlyList<UserAccessEntry> MakeConsolidationTestEntries()
    {
        // Entry 1 + Entry 2: same user/permission/accesstype/grantedthrough, different sites
        var e1 = MakeEntry(
            userDisplay: "Bob Jones",
            userLogin: "bob@contoso.com",
            siteUrl: "https://contoso.sharepoint.com/sites/Alpha",
            siteTitle: "Alpha Site",
            permLevel: "Contribute",
            accessType: AccessType.Direct,
            grantedThrough: "Direct Permissions");

        var e2 = MakeEntry(
            userDisplay: "Bob Jones",
            userLogin: "bob@contoso.com",
            siteUrl: "https://contoso.sharepoint.com/sites/Beta",
            siteTitle: "Beta Site",
            permLevel: "Contribute",
            accessType: AccessType.Direct,
            grantedThrough: "Direct Permissions");

        // Entry 3: different user — will have 1 location
        var e3 = MakeEntry(
            userDisplay: "Carol Davis",
            userLogin: "carol@contoso.com",
            siteUrl: "https://contoso.sharepoint.com/sites/Gamma",
            siteTitle: "Gamma Site",
            permLevel: "Read",
            accessType: AccessType.Group,
            grantedThrough: "Readers Group");

        return new[] { e1, e2, e3 };
    }

    // RPT-03-b: BuildHtml(entries, mergePermissions: false) is byte-identical to BuildHtml(entries)
    [Fact]
    public void BuildHtml_mergePermissionsFalse_identical_to_default()
    {
        var entries = MakeConsolidationTestEntries();
        var svc = new UserAccessHtmlExportService();

        var defaultOutput = svc.BuildHtml(entries);
        var explicitFalse = svc.BuildHtml(entries, mergePermissions: false);

        Assert.Equal(defaultOutput, explicitFalse);
    }

    // RPT-03-c: BuildHtml(entries, mergePermissions: true) contains "Sites" column header and consolidated content
    [Fact]
    public void BuildHtml_mergePermissionsTrue_contains_sites_column()
    {
        var entries = MakeConsolidationTestEntries();
        var svc = new UserAccessHtmlExportService();

        var html = svc.BuildHtml(entries, mergePermissions: true);

        Assert.Contains("Sites", html);
        // Consolidated rows present for both users
        Assert.Contains("Bob Jones", html);
        Assert.Contains("Carol Davis", html);
    }

    // RPT-03-d: 2+ locations produce [N sites] badge with toggleGroup and hidden sub-rows
    [Fact]
    public void BuildHtml_mergePermissionsTrue_multiLocation_has_badge_and_subrows()
    {
        var entries = MakeConsolidationTestEntries();
        var svc = new UserAccessHtmlExportService();

        var html = svc.BuildHtml(entries, mergePermissions: true);

        // Badge with onclick
        Assert.Contains("onclick=\"toggleGroup('loc", html);
        // Hidden sub-rows
        Assert.Contains("data-group=\"loc", html);
    }

    // RPT-03-e: mergePermissions=true omits "By Site" button and view-site div
    [Fact]
    public void BuildHtml_mergePermissionsTrue_omits_bysite_view()
    {
        var entries = MakeConsolidationTestEntries();
        var svc = new UserAccessHtmlExportService();

        var html = svc.BuildHtml(entries, mergePermissions: true);

        Assert.DoesNotContain("btn-site", html);
        Assert.DoesNotContain("view-site", html);
    }
}
