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

namespace e_suite.Modules.OrganisationsManager;

public class OrganisationsManager : IOrganisationsManager
{
    private readonly IOrganisationsManagerRepository _organisationsManagerRepository;
    private readonly IEFlowSyncMessageSender _eFlowSyncMessageSender;

    public OrganisationsManager(IOrganisationsManagerRepository organisationsManagerRepository, IEFlowSyncMessageSender eFlowSyncMessageSender)
    {
        _organisationsManagerRepository = organisationsManagerRepository;
        _eFlowSyncMessageSender = eFlowSyncMessageSender;
    }

    public async Task<Organisation?> GetOrganisation(GeneralIdRef generalIdRef, CancellationToken cancellationToken)
    {
        return await _organisationsManagerRepository.FindOrganisation(generalIdRef, cancellationToken);
    }

    public async Task AddOrganisation(AuditUserDetails auditUserDetails, CreateOrganisation addOrganisationDto, bool triggerEFlowSync, CancellationToken cancellationToken)
    {
        await _organisationsManagerRepository.TransactionAsync(async () =>
        {
            var duplicateGuid = addOrganisationDto.Guid == null ? null : await _organisationsManagerRepository.FindOrganisation(new GeneralIdRef { Guid = addOrganisationDto.Guid }, cancellationToken);
            if (duplicateGuid != null)
                throw new ArgumentException($"Guid {addOrganisationDto.Guid} is already used.");

            if (await _organisationsManagerRepository.GetOrganisationsList().FirstOrDefaultAsync( x => x.Name == addOrganisationDto.Name, cancellationToken) != null)
                throw new ArgumentException($"{addOrganisationDto.Name} is already used.");

            var organisation = new Organisation
            {
                Guid = addOrganisationDto.Guid ?? Guid.NewGuid(),
                Name = addOrganisationDto.Name,
                Address = addOrganisationDto.Address,
                Status = addOrganisationDto.Status
            };

            await _organisationsManagerRepository.AddOrganisation(auditUserDetails, organisation, cancellationToken);
            if (triggerEFlowSync)
                _eFlowSyncMessageSender.SyncEFlowPrinterCategories();
        });
    }

    public async Task<PaginatedData<ReadOrganisation>> GetOrganisationList( Paging paging, CancellationToken cancellationToken )
    {
        var organisations = _organisationsManagerRepository.GetOrganisationsList();

        var paginatedData = await PaginatedData.Paginate<Organisation, ReadOrganisation>(organisations, paging,
            KeySelector, cancellationToken);

        return paginatedData;
    }

    private Expression<Func<Organisation, object>> KeySelector(string? sortKey)
    {
        return sortKey?.ToLowerInvariant() switch
        {
            "id" => x => x.Id,
            "guid" => x => x.Guid,
            "name" => x => x.Name,
            "address" => x => x.Address,
            "status" => x => x.Status.ToString().ToLower(),
            _ => x => x.Name
        };
    }

    public async Task DeleteOrganisation(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, bool triggerEFlowSync, CancellationToken cancellationToken)
    {
        var existingOrganisation = await _organisationsManagerRepository.FindOrganisation(generalIdRef, cancellationToken);

        if (existingOrganisation == null)
            throw new NotFoundException("Organisation with this Id was not foud.");

        await _organisationsManagerRepository.DeleteOrganisation(auditUserDetails, generalIdRef, cancellationToken);
        if (triggerEFlowSync)
            _eFlowSyncMessageSender.SyncEFlowPrinterCategories();
    }

    public async Task EditOrganisation(AuditUserDetails auditUserDetails, EditOrganisation editOrganisationDto, bool triggerEFlowSync, CancellationToken cancellationToken)
    {
        var existingOrganisation = await _organisationsManagerRepository.FindOrganisation(editOrganisationDto.GeneralIdRef, cancellationToken);

        if (existingOrganisation == null)
            throw new NotFoundException("Organisation with this Id was not foud.");

        existingOrganisation.Name = editOrganisationDto.Name;
        existingOrganisation.Address = editOrganisationDto.Address;
        existingOrganisation.Status = editOrganisationDto.Status;

        await _organisationsManagerRepository.EditOrganisation(auditUserDetails, existingOrganisation, cancellationToken);
        if (triggerEFlowSync)
            _eFlowSyncMessageSender.SyncEFlowPrinterCategories();
    }
}