using IWS5.Core.Enums;
using IWS5.Core.Models.Request;
using IWS5.Core.Models.Request.Process;
using IWS5.DatabaseAccess.Interfaces;
using IWS5.DatabaseAccess.IwsDataContext;
using IWS5.DatabaseAccess.IwsDataModels;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Linq.Dynamic.Core;
using IWS5.Core.DTO;

namespace IWS5.DatabaseAccess.Repositories;

public class ProcessRepository(GreenfieldPd2015Context context, ILogger logger)
    : IWSRepository<AtprocessDefinition>(context), IProcessRepository
{
    public async Task<IReadOnlyCollection<AtprocessDefinition>> GetAllowedProcesses(string userName, bool latestVersionOnly = false)
    {
        logger.Information("Starting GetAllowedProcesses for User {UserName}", userName);

        var allowedProcessGuids = context.Atusers
            .AsNoTracking()
            .Where(user => user.UserName.ToLower() == userName.ToLower())
            .SelectMany(user => user.Parents.SelectMany(parent => parent.Atrights))
            .Where(right => right.ObjectType == (int)ObjectType.Process && right.AllowRights == (int)ATRightAllowRight.UseProcess)
            .Select(right => right.ObjectGuid)
            .Distinct();

        var processQuery = context.AtprocessDefinitions
            .AsNoTracking()
            .Where(process => allowedProcessGuids.Contains(process.ObjectId) && process.Status == 1 && process.ProcessType == 0);

        if (latestVersionOnly)
        {
            processQuery = processQuery
                .GroupBy(process => process.ProcessName)
                .Select(group => group.OrderByDescending(process => process.ProcessVersion).First());
        }

        var processQueryString = processQuery.ToQueryString();
        logger.Information("Generated SQL query: {Query}", processQueryString);
        
        var processes = await processQuery
            .OrderByDescending(process => process.ProcessVersion)
            .ThenBy(process => process.ProcessName)
            .ThenBy(process => process.ProcessGroupId)
            .ToListAsync();

        logger.Information("Successfully retrieved {ProcessCount} processes for User {UserName}", processes.Count, userName);

        return processes.AsReadOnly();
    }

    public async Task<ATRightAllowRight[]> ListProcessesAllowedRights(ListProcessAllowedRights request)
    {
        logger.Information("Starting ListProcessesAllowedRights for ProcessId {ProcessId} and UserName {UserName}", request.ProcessId, request.UserName);

        var processGuid = await context.AtprocessDefinitions.AsNoTracking()
            .Where(process => process.ProcessId == request.ProcessId)
            .Select(process => process.ObjectId)
            .FirstOrDefaultAsync();

        if (processGuid == default)
        {
            logger.Warning("No process found for ProcessId {ProcessId}", request.ProcessId);
            return [];
        }

        logger.Information("Found process GUID: {ProcessGuid} for ProcessId {ProcessId}", processGuid, request.ProcessId);

        var userRightsQuery = context.Atusers
            .Where(user => user.UserName == request.UserName)
            .SelectMany(user => user.Parents)
            .SelectMany(parent => parent.Atrights
                .Where(right => right.ObjectGuid == processGuid))
            .Select(right => right.AllowRights)
            .Distinct();

        logger.Information("Generated SQL query for user rights: {Query}", userRightsQuery.ToQueryString());

        var userRights = await userRightsQuery.ToArrayAsync();

        logger.Information("Successfully retrieved {RightsCount} rights for ProcessId {ProcessId} and UserName {UserName}", userRights.Length, request.ProcessId, request.UserName);

        return userRights.Select(right => (ATRightAllowRight)right).ToArray();
    }

    public async Task<AtprocessDefinition> ListProcessesAllowedTemplates(
        ListProcessAllowedTemplatesRequest listProcessAllowedTemplatesRequest)
    {
        logger.Information("Starting ListProcessesAllowedTemplates for ProcessId {ProcessId}",
            listProcessAllowedTemplatesRequest.ProcessId);

        var query = context.AtprocessDefinitions
            .Where(process => process.ProcessId == listProcessAllowedTemplatesRequest.ProcessId)
            .Include(process => process.DefaultDocTemplateNavigation)
            .Include(process => process.AtprocessDocumentTemplateRights
                .Where(right => right.AllowRights == (int)ATRightAllowRight.ProcessUseTemplate))
            .ThenInclude(right => right.DocumentTemplateObject)
            .Select(process => new AtprocessDefinition
            {
                ProcessId = process.ProcessId,
                ProcessName = process.ProcessName,
                DefaultDocTemplate = process.DefaultDocTemplate,
                AtprocessDocumentTemplateRights = process.AtprocessDocumentTemplateRights.Select(right =>
                    new AtprocessDocumentTemplateRight
                    {
                        DocumentTemplateObjectid = right.DocumentTemplateObjectid,
                        DocumentTemplateObject = new AtdocumentCabinet
                        {
                            CabinetId = right.DocumentTemplateObject.CabinetId,
                            CabinetName = right.DocumentTemplateObject.CabinetName
                        }
                    }).ToList()
            });

        logger.Information("Generated SQL query: {Query}", query.ToQueryString());

        var result = await query.FirstOrDefaultAsync();
        if (result == null)
        {
            logger.Warning("No process found for ProcessId {ProcessId}", listProcessAllowedTemplatesRequest.ProcessId);
        }
        else
        {
            logger.Information("Successfully retrieved process details for ProcessId {ProcessId}",
                listProcessAllowedTemplatesRequest.ProcessId);
        }

        return result ?? new AtprocessDefinition();
    }

    public async Task<int> UpdateProcessDefaultTemplate(UpdateProcessDefaultTemplateRequest request)
    {
        logger.Information("Starting UpdateProcessDefaultTemplate for ProcessId {ProcessId}", request.ProcessId);

        var process = context.AtprocessDefinitions
            .FirstOrDefault(process => process.ObjectId == request.ProcessId);

        if (process == null)
        {
            logger.Warning("Process with ProcessId {ProcessId} not found", request.ProcessId);
            return 0;
        }

        var template = context.AtdocumentCabinets
            .FirstOrDefault(template => template.ObjectId == request.DocumentTemplateTypeId);

        if (template == null)
        {
            logger.Warning("Document template with DocumentTemplateTypeId {DocumentTemplateTypeId} not found",
                request.DocumentTemplateTypeId);
            return 0;
        }

        process.DefaultDocTemplate = template.CabinetId;

        var result = await context.SaveChangesAsync();

        logger.Information("Successfully updated DefaultDocTemplate for ProcessId {ProcessId} to {DefaultDocTemplate}",
            request.ProcessId, template.CabinetId);

        return result;
    }
}