// Harness di test leggero per plugin e Custom Workflow Activity Dataverse.
// Nessuna dipendenza esterna (no FakeXrmEasy). Generato da Dataverse Helper.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;

namespace DataverseHelper.Testing
{
    /// <summary>Fake IOrganizationService che registra le operazioni e consente di simularne i risultati.</summary>
    public class FakeOrganizationService : IOrganizationService
    {
        public readonly List<Entity> Created = new List<Entity>();
        public readonly List<Entity> Updated = new List<Entity>();
        public readonly List<EntityReference> Deleted = new List<EntityReference>();

        public Func<string, Guid, ColumnSet, Entity> OnRetrieve;
        public Func<QueryBase, EntityCollection> OnRetrieveMultiple;
        public Func<OrganizationRequest, OrganizationResponse> OnExecute;

        public Guid Create(Entity entity)
        {
            if (entity.Id == Guid.Empty) entity.Id = Guid.NewGuid();
            Created.Add(entity);
            return entity.Id;
        }

        public Entity Retrieve(string entityName, Guid id, ColumnSet columnSet)
            => OnRetrieve != null ? OnRetrieve(entityName, id, columnSet) : new Entity(entityName, id);

        public void Update(Entity entity) => Updated.Add(entity);

        public void Delete(string entityName, Guid id) => Deleted.Add(new EntityReference(entityName, id));

        public EntityCollection RetrieveMultiple(QueryBase query)
            => OnRetrieveMultiple != null ? OnRetrieveMultiple(query) : new EntityCollection();

        public OrganizationResponse Execute(OrganizationRequest request)
            => OnExecute != null ? OnExecute(request) : new OrganizationResponse();

        public void Associate(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities) { }
        public void Disassociate(string entityName, Guid entityId, Relationship relationship, EntityReferenceCollection relatedEntities) { }
    }

    public class FakeTracingService : ITracingService
    {
        public readonly StringBuilder Log = new StringBuilder();
        public void Trace(string format, params object[] args)
            => Log.AppendLine(args != null && args.Length > 0 ? string.Format(format, args) : format);
    }

    public class FakeServiceProvider : IServiceProvider
    {
        private readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();
        public void Add<T>(T implementation) => _services[typeof(T)] = implementation;
        public object GetService(Type serviceType)
            => _services.TryGetValue(serviceType, out var s) ? s : null;
    }

    public class FakeServiceFactory : IOrganizationServiceFactory
    {
        private readonly IOrganizationService _service;
        public FakeServiceFactory(IOrganizationService service) { _service = service; }
        public IOrganizationService CreateOrganizationService(Guid? userId) => _service;
    }

    public class FakePluginExecutionContext : IPluginExecutionContext
    {
        public int Stage { get; set; } = 40;
        public IPluginExecutionContext ParentContext { get; set; }
        public int Mode { get; set; }
        public int IsolationMode { get; set; }
        public int Depth { get; set; } = 1;
        public string MessageName { get; set; } = "";
        public string PrimaryEntityName { get; set; } = "";
        public Guid? RequestId { get; set; }
        public string SecondaryEntityName { get; set; } = "";
        public ParameterCollection InputParameters { get; set; } = new ParameterCollection();
        public ParameterCollection OutputParameters { get; set; } = new ParameterCollection();
        public ParameterCollection SharedVariables { get; set; } = new ParameterCollection();
        public Guid UserId { get; set; } = Guid.NewGuid();
        public Guid InitiatingUserId { get; set; } = Guid.NewGuid();
        public Guid BusinessUnitId { get; set; } = Guid.NewGuid();
        public Guid OrganizationId { get; set; } = Guid.NewGuid();
        public string OrganizationName { get; set; } = "org";
        public Guid PrimaryEntityId { get; set; }
        public EntityImageCollection PreEntityImages { get; set; } = new EntityImageCollection();
        public EntityImageCollection PostEntityImages { get; set; } = new EntityImageCollection();
        public EntityReference OwningExtension { get; set; }
        public Guid CorrelationId { get; set; } = Guid.NewGuid();
        public bool IsExecutingOffline { get; set; }
        public bool IsOfflinePlayback { get; set; }
        public bool IsInTransaction { get; set; }
        public Guid OperationId { get; set; }
        public DateTime OperationCreatedOn { get; set; } = DateTime.UtcNow;
    }

    /// <summary>Builder per testare un plugin: contesto, service e tracing già pronti.</summary>
    public class PluginTestContext
    {
        public FakeOrganizationService Service { get; } = new FakeOrganizationService();
        public FakeTracingService Tracing { get; } = new FakeTracingService();
        public FakePluginExecutionContext Context { get; } = new FakePluginExecutionContext();

        public IServiceProvider BuildServiceProvider()
        {
            var sp = new FakeServiceProvider();
            sp.Add<IPluginExecutionContext>(Context);
            sp.Add<ITracingService>(Tracing);
            sp.Add<IOrganizationServiceFactory>(new FakeServiceFactory(Service));
            return sp;
        }

        public void Execute(IPlugin plugin) => plugin.Execute(BuildServiceProvider());

        public PluginTestContext WithMessage(string message) { Context.MessageName = message; return this; }
        public PluginTestContext WithStage(int stage) { Context.Stage = stage; return this; }
        public PluginTestContext WithTarget(Entity target)
        {
            Context.InputParameters["Target"] = target;
            Context.PrimaryEntityName = target.LogicalName;
            Context.PrimaryEntityId = target.Id;
            return this;
        }
        public PluginTestContext WithTargetReference(EntityReference target)
        {
            Context.InputParameters["Target"] = target;
            Context.PrimaryEntityName = target.LogicalName;
            Context.PrimaryEntityId = target.Id;
            return this;
        }
        public PluginTestContext WithPreImage(string alias, Entity image) { Context.PreEntityImages[alias] = image; return this; }
        public PluginTestContext WithPostImage(string alias, Entity image) { Context.PostEntityImages[alias] = image; return this; }
    }

    /// <summary>Fake IWorkflowContext per testare le Custom Workflow Activity con WorkflowInvoker.</summary>
    public class FakeWorkflowContext : IWorkflowContext
    {
        public string StageName { get; set; } = "";
        public int WorkflowCategory { get; set; }
        public int WorkflowMode { get; set; }
        public IWorkflowContext ParentContext { get; set; }
        public int Mode { get; set; }
        public int IsolationMode { get; set; }
        public int Depth { get; set; } = 1;
        public string MessageName { get; set; } = "";
        public string PrimaryEntityName { get; set; } = "";
        public Guid? RequestId { get; set; }
        public string SecondaryEntityName { get; set; } = "";
        public ParameterCollection InputParameters { get; set; } = new ParameterCollection();
        public ParameterCollection OutputParameters { get; set; } = new ParameterCollection();
        public ParameterCollection SharedVariables { get; set; } = new ParameterCollection();
        public Guid UserId { get; set; } = Guid.NewGuid();
        public Guid InitiatingUserId { get; set; } = Guid.NewGuid();
        public Guid BusinessUnitId { get; set; } = Guid.NewGuid();
        public Guid OrganizationId { get; set; } = Guid.NewGuid();
        public string OrganizationName { get; set; } = "org";
        public Guid PrimaryEntityId { get; set; }
        public EntityImageCollection PreEntityImages { get; set; } = new EntityImageCollection();
        public EntityImageCollection PostEntityImages { get; set; } = new EntityImageCollection();
        public EntityReference OwningExtension { get; set; }
        public Guid CorrelationId { get; set; } = Guid.NewGuid();
        public bool IsExecutingOffline { get; set; }
        public bool IsOfflinePlayback { get; set; }
        public bool IsInTransaction { get; set; }
        public Guid OperationId { get; set; }
        public DateTime OperationCreatedOn { get; set; } = DateTime.UtcNow;
    }
}
