using IWS5.Core.DTO;
using IWS5.Core.Enums;
using IWS5.Core.Models;
using IWS5.Core.Models.Request;
using IWS5.Core.Services;
using IWS5.DatabaseAccess.Interfaces;
using Serilog;

namespace IWS5.Services;
public class TrackingRecordService(ITrackingRecordRepository trackingRecordRepository,
    ILogger logger,
    IEventDefinitionRepository eventDefinitionRepository,
    IWorkItemReferenceRepository workItemReferenceRepository,
    IProcessRepository processRepository,
    IRightsRepository rightsRepository,
    IQueueRepository queueRepository)
    : ITrackingRecordService
{

    public async Task<CodeResponse<List<TrackingRecordListItemDTO>>> ListTrackingRecords(TrackingRecordRequest trackingRecordRequest)
    {
        logger.Information("Starting tracking retrieval for WorkItemReferenceId {WorkItemReferenceId} requested by User {UserName}", trackingRecordRequest.WorkItemReferenceId, trackingRecordRequest.UserName);

        try
        {
            if (trackingRecordRequest.WorkItemReferenceId == 0)
            {
                return new CodeResponse<List<TrackingRecordListItemDTO>>(ResponseCode.BadRequest, "Work Item Reference Id should be greater than null", null);
            }
            
            var workItemReference = await workItemReferenceRepository.FindAsync(wReference => wReference.WorkItemReferenceId == trackingRecordRequest.WorkItemReferenceId, wRef => wRef.WorkItem);
            var queue = await queueRepository.FindAsync(q => q.Id == workItemReference.QueueId);
            var inboxEvent = await eventDefinitionRepository
                .FindAsync(atEvent => atEvent.EventId == queue.EventId 
                                      && (atEvent.EventTypeId == (int)ATEventType.User || atEvent.EventTypeId == (int)ATEventType.Queue),
                    atEvent => atEvent.AtinboxEvents);

            if (inboxEvent.EventTypeId == (int)ATEventType.User)
            {
                if (workItemReference.AssignedTo != null && !workItemReference.AssignedTo.Equals(trackingRecordRequest.UserName, StringComparison.CurrentCultureIgnoreCase))
                {
                    logger.Information("User {UserName} is assigned to inbox {QueueId}", trackingRecordRequest.UserName, workItemReference.QueueId);
                    return new CodeResponse<List<TrackingRecordListItemDTO>>(ResponseCode.Unauthorized, $"User {trackingRecordRequest.UserName} is assigned to inbox {workItemReference.QueueId}");
                }
            }
            else if (inboxEvent.EventTypeId == (int)ATEventType.Queue)
            {
                var process = await processRepository.FindAsync(process => process.ProcessId == workItemReference.WorkItem.ProcessId);
                var rights = await rightsRepository.ListUserRelatedRights(trackingRecordRequest.UserName, ObjectType.Process, ATRightAllowRight.ViewTracking, process.ObjectId);
                
                if (rights.Count == 0 && workItemReference.AssignedTo != trackingRecordRequest.UserName)
                {
                    logger.Warning("User {UserName} does not have rights to view tracking for WorkItemReferenceId {WorkItemReferenceId}", trackingRecordRequest.UserName, trackingRecordRequest.WorkItemReferenceId);
                    return new CodeResponse<List<TrackingRecordListItemDTO>>(ResponseCode.Unauthorized, "User does not have rights to view tracking", null);
                }
            }
            else
            {
                logger.Warning("EventTypeId {EventTypeId} for Event {EventName} is not recognized", inboxEvent.EventTypeId, inboxEvent.EventName);
                return new CodeResponse<List<TrackingRecordListItemDTO>>(ResponseCode.BadRequest, "Event type is not recognized", null);
            }
            
            var dataResponse = await trackingRecordRepository.ListAsync(record => record.WorkItemId == workItemReference.WorkItemId, record => record.TrackingDefinition, record => record.LastAction);
            logger.Information("Successfully retrieved tracking data for WorkItemReferenceId {WorkItemReferenceId}", trackingRecordRequest.WorkItemReferenceId);
            var dataResult = new List<TrackingRecordListItemDTO>();
            foreach (var record in dataResponse)
            {
                var dto = new TrackingRecordListItemDTO
                {
                    Id = record.Id,
                    TrackingDate = record.TrackingDate,
                    WorkItemId = record.WorkItemId,
                    EventId = record.TrackingDefinition.EventId,
                    UserName = record.UserName,
                    LastAction = record.LastAction?.ButtonName,
                    Message = record.Message,
                    Description = record.TrackingDefinition.Description
                };
                dataResult.Add(dto);
                
            }

            return new CodeResponse<List<TrackingRecordListItemDTO>>(ResponseCode.OK, null, dataResult);
        }
        catch (Exception ex)
        {
            logger.Error(ex, "Error occurred while retrieving tracking for WorkItemId {WorkItemReferenceId} requested by User {UserName}", trackingRecordRequest.WorkItemReferenceId, trackingRecordRequest.UserName);
            throw;
        }
    }
}