using e_suite.API.Common;
using e_suite.API.Common.exceptions;
using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Database.Core.Extensions;
using e_suite.Database.Core.Tables.Activity;
using e_suite.Messaging.Common;
using e_suite.Utilities.Pagination;
using eSuite.Core.Enums;
using eSuite.Core.Miscellaneous;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;

namespace e_suite.Modules.RunningActivityManager;

public class ActivityManager : IActivityManager
{
    private readonly IActivityMessageSender _activityMessageSender;
    private readonly IActivityRepository _activityRepository;
    private readonly IWorkflowTemplateRepository _workflowTemplateRepository;

    public ActivityManager(IActivityMessageSender activityMessageSender, IActivityRepository activityRepository, IWorkflowTemplateRepository workflowTemplateRepository)
    {
        _activityMessageSender = activityMessageSender;
        _activityRepository = activityRepository;
        _workflowTemplateRepository = workflowTemplateRepository;
    }

    public async Task CreateActivity(
        AuditUserDetails auditUserDetails,
        CreateActivity template,
        CancellationToken cancellationToken
    )
    {
        GeneralIdRef? progressActivityRef = null;
        await _activityRepository.TransactionAsync(async () =>
        {
            var workflowTemplate = _workflowTemplateRepository.GetWorkflows().Include( x => x.Versions).FindByGeneralIdRef(template.WorkflowTemplateId);
            var workflowTemplateVersion = workflowTemplate!.Versions.OrderByDescending(x => x.Version).First();

            Activity activity = new Activity
            {
                Guid = Guid.NewGuid(),
                Name = template.ActivityName,
                WorkflowVersion = workflowTemplateVersion,
            };

            await _activityRepository.CreateActivityInstanceAsync(auditUserDetails, activity, cancellationToken);

            progressActivityRef = activity.ToGeneralIdRef();
        });

        if (progressActivityRef != null)
            _activityMessageSender.ProgressActivity(progressActivityRef);
    }

    public async Task<PaginatedData<GetMyAssignments>> GetMyActiveAssignmentsAsync(AuditUserDetails auditUserDetails, Paging paging, CancellationToken cancellationToken)
    {
        var myAssignments = _activityRepository.GetAssignments()
            .Where(x => 
                x.Deleted == false && 
                x.ActivityState == ActivityState.Active && 
                (x.UserId == auditUserDetails.UserId
                || x.Role.UserRoles.Any(u => u.UserId == auditUserDetails.UserId)));

        var paginatedData = await PaginatedData.Paginate<ActivityAssignment, GetMyAssignments>(myAssignments, paging,
            MyActiveAssignmentsKeySelector, cancellationToken);

        return paginatedData;
    }
    
    private Expression<Func<ActivityAssignment, object>> MyActiveAssignmentsKeySelector(string sortKey)
    {
        return sortKey?.ToLowerInvariant() switch
        {
            "id" => x => x.Id,
            "guid" => x => x.Guid,
            "taskname" => x => x.Task.TaskName,
            "startdatetime" => x => x.StartDateTime,
            _ => x => x.Id
        };
    }

    public async Task<AssignmentDetails> GetAssignmentDetails(
        AuditUserDetails auditUserDetails,
        GeneralIdRef assignmentId,
        CancellationToken cancellationToken
    )
    {
        var assignment = await _activityRepository.GetAssignments().FindByGeneralIdRefAsync(assignmentId, cancellationToken);
        if (assignment == null)
            throw new NotFoundException("Unable to find assignment");



        var assignmentObject = new GetAssignment(assignment);

        var taskObject = new GetTask(assignment.Task);

        var activityObject = new GetActivity(assignment.Task.Activity);

        var dbWorkflowTemplate = _workflowTemplateRepository.GetWorkflowVersions().Single(x => x.Id == activityObject.WorkflowVersionId);
        
        return new AssignmentDetails
        {
            Assignment = assignmentObject,
            Task = taskObject,
            Activity = activityObject,
            WorkflowTemplateVersion = new GetWorkflowTemplateVersion(dbWorkflowTemplate)
            //todo add the assignment metadata (outcomes)
        };
    }
}