using System.Net;
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Tiku.Api.Contracts;
using Tiku.Application.PlatformAdmin;
using Tiku.Application.Security;
using Tiku.Domain.Common;
using Tiku.Domain.Identity;
using Tiku.Domain.Operations;
using Tiku.Domain.Platform;
using Tiku.Domain.Tenancy;
using Tiku.Infrastructure.Persistence;

namespace Tiku.IntegrationTests.Api;

public sealed class PlatformApprovalEndpointTests
{
    private static readonly JsonSerializerOptions JsonOptions = CreateJsonOptions();

    [Fact]
    public async Task Tenant_archive_requires_another_authorized_operator_and_executes_once()
    {
        await using var factory = new ApiTestFactory(configurationOverrides: new Dictionary<string, string?>
        {
            ["Tenancy:Resolution:PlatformHosts:0"] = "localhost"
        });
        var permissions = new[]
        {
            BackendPermissions.PlatformDashboardView,
            BackendPermissions.PlatformTenantManage,
            BackendPermissions.PlatformApprovalView,
            BackendPermissions.PlatformApprovalDecide
        };
        var (requester, approver) = await SeedActorsAsync(factory, permissions);
        var tenant = new Tenant
        {
            Slug = $"approval-{Guid.NewGuid():N}"[..30], Name = "Approval Tenant", Status = TenantStatus.Active,
            BillingStatus = BillingStatus.Active
        };
        await factory.SeedAsync(
            tenant,
            new PlatformApprovalPolicy
            {
                Code = PlatformApprovalPolicyCodes.TenantArchive,
                Name = "Tenant archive",
                RequiredPermission = BackendPermissions.PlatformTenantManage,
                AlwaysRequireApproval = true
            });

        using var requesterClient = factory.CreateClient();
        requesterClient.UseAccessToken(await requesterClient.LoginAsPlatformAsync(requester.Email));
        using var archive = new HttpRequestMessage(HttpMethod.Patch, "/api/platform/tenants/status")
        {
            Content = JsonContent.Create(new UpdatePlatformTenantStatusDto
                { TenantId = tenant.Id, Status = TenantStatus.Archived, Reason = "Close expired customer" })
        };
        archive.Headers.Add("Idempotency-Key", "archive-approval-1");
        var submission = await requesterClient.SendAsync(archive);
        Assert.Equal(HttpStatusCode.Accepted, submission.StatusCode);
        var body = await submission.Content.ReadFromJsonAsync<PlatformCommandSubmission>(JsonOptions);
        Assert.NotNull(body?.ApprovalRequest);

        var selfApproval = await requesterClient.PostAsJsonAsync(
            $"/api/platform/approvals/{body.ApprovalRequest.Id}/approve",
            new PlatformApprovalDecisionDto("Self approval"));
        Assert.Equal(HttpStatusCode.Conflict, selfApproval.StatusCode);

        using var approverClient = factory.CreateClient();
        approverClient.UseAccessToken(await approverClient.LoginAsPlatformAsync(approver.Email));
        var approval = await approverClient.PostAsJsonAsync(
            $"/api/platform/approvals/{body.ApprovalRequest.Id}/approve",
            new PlatformApprovalDecisionDto("Independent verification complete"));
        Assert.Equal(HttpStatusCode.OK, approval.StatusCode);
        var approved = await approval.Content.ReadFromJsonAsync<PlatformApprovalRequestItem>(JsonOptions);
        Assert.Equal(PlatformApprovalRequestStatus.Approved, approved?.Status);

        using (var executionScope = factory.CreateSystemScope("Execute approved platform command"))
        {
            var processor = executionScope.ServiceProvider.GetRequiredService<IPlatformApprovalService>();
            Assert.Equal(1, await processor.ProcessApprovedAsync());
        }

        var replay = await approverClient.PostAsJsonAsync($"/api/platform/approvals/{body.ApprovalRequest.Id}/approve",
            new PlatformApprovalDecisionDto("Replay"));
        Assert.Equal(HttpStatusCode.Conflict, replay.StatusCode);
        using var scope = factory.CreateSystemScope("Verify platform approval execution");
        var db = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
        var executed = await db.PlatformApprovalRequests.AsNoTracking()
            .SingleAsync(item => item.Id == body.ApprovalRequest.Id);
        Assert.True(executed.Status == PlatformApprovalRequestStatus.Succeeded,
            $"Approval execution failed: {executed.Error}");
        Assert.Equal(TenantStatus.Archived,
            await db.Tenants.Where(item => item.Id == tenant.Id).Select(item => item.Status).SingleAsync());
        Assert.True(await db.AuditLogs.AnyAsync(item =>
            item.Action == "platform.approval.executed" && item.TargetId == body.ApprovalRequest.Id.ToString("N")));
    }

    private static async Task<((Guid UserId, string Email) Requester, (Guid UserId, string Email) Approver)>
        SeedActorsAsync(
            ApiTestFactory factory,
            IReadOnlyCollection<string> permissionCodes)
    {
        var requester = (UserId: Guid.NewGuid(), Email: $"approval-requester-{Guid.NewGuid():N}@example.test");
        var approver = (UserId: Guid.NewGuid(), Email: $"approval-approver-{Guid.NewGuid():N}@example.test");
        var requesterRole = Guid.NewGuid();
        var approverRole = Guid.NewGuid();
        var modules = permissionCodes.Select(PermissionModuleCatalog.ResolvePermissionModuleCode)
            .Distinct(StringComparer.Ordinal)
            .Select(code => new PermissionModule
            {
                Code = code, Name = code, Area = BackendPermissionArea.Platform,
                RequiredFeatureCode = PermissionModuleCatalog.RequiredFeatures[code]
            });
        var permissions = permissionCodes.Select(code => new BackendPermission
        {
            Code = code, Name = code, Area = BackendPermissionArea.Platform,
            PermissionModuleCode = PermissionModuleCatalog.ResolvePermissionModuleCode(code), IsSystem = true
        });
        await factory.SeedAsync([
            .. modules, .. permissions,
            User(requester.UserId, requester.Email), User(approver.UserId, approver.Email),
            new PlatformBackendRole
            {
                Id = requesterRole, Code = $"approval_requester_{requesterRole:N}", Name = "Approval Requester",
                Status = BackendRoleStatus.Active
            },
            new PlatformBackendRole
            {
                Id = approverRole, Code = $"approval_approver_{approverRole:N}", Name = "Approval Approver",
                Status = BackendRoleStatus.Active
            },
            .. permissionCodes.Select(code => new PlatformBackendRolePermission
                { RoleId = requesterRole, PermissionCode = code }),
            .. permissionCodes.Select(code => new PlatformBackendRolePermission
                { RoleId = approverRole, PermissionCode = code }),
            new PlatformBackendUserRole { UserId = requester.UserId, RoleId = requesterRole },
            new PlatformBackendUserRole { UserId = approver.UserId, RoleId = approverRole }
        ]);
        return (requester, approver);
    }

    private static User User(Guid id, string email)
    {
        return new User
        {
            Id = id,
            Email = email,
            NormalizedEmail = email.ToUpperInvariant(),
            UserName = email,
            NormalizedUserName = email.ToUpperInvariant(),
            Name = email,
            PrimaryRole = "platform_admin",
            RawProfile = JsonDefaults.Object()
        }.WithTestPassword();
    }

    private static JsonSerializerOptions CreateJsonOptions()
    {
        var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
        options.Converters.Add(new JsonStringEnumConverter());
        return options;
    }
}