using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OmsorgCore.Application.Abstractions;
using OmsorgCore.Infrastructure.Persistence;
using OmsorgCore.Infrastructure.Repositories;
using OmsorgCore.Infrastructure.Security;
using OmsorgCore.Infrastructure.Storage;

namespace OmsorgCore.Infrastructure;

/// <summary>
/// Bündelt die Registrierung aller Infrastructure-Implementierungen, damit Program.cs
/// nur diese eine Erweiterungsmethode aufrufen muss statt jede Klasse einzeln zu verdrahten.
/// </summary>
public static class DependencyInjection
{
    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddScoped<AuditSaveChangesInterceptor>();

        services.AddDbContext<OmsorgCoreDbContext>((sp, options) =>
            options.UseNpgsql(configuration.GetConnectionString("OmsorgCore"))
                   .AddInterceptors(sp.GetRequiredService<AuditSaveChangesInterceptor>()));

        services.Configure<JwtOptions>(configuration.GetSection(JwtOptions.SectionName));
        services.Configure<RefreshTokenOptions>(configuration.GetSection(RefreshTokenOptions.SectionName));
        services.Configure<PasswordResetOptions>(configuration.GetSection(PasswordResetOptions.SectionName));
        services.Configure<SeedOptions>(configuration.GetSection(SeedOptions.SectionName));
        services.Configure<LoginAttemptOptions>(configuration.GetSection(LoginAttemptOptions.SectionName));
        services.Configure<PasswordPolicyOptions>(configuration.GetSection(PasswordPolicyOptions.SectionName));
        services.Configure<StorageOptions>(configuration.GetSection(StorageOptions.SectionName));

        services.AddScoped<IEmployeeRepository, EmployeeRepository>();
        services.AddScoped<IFacilityRepository, FacilityRepository>();
        services.AddScoped<IFacilityContactRepository, FacilityContactRepository>();
        services.AddScoped<IFacilityQualificationRateRepository, FacilityQualificationRateRepository>();
        services.AddScoped<IEmployeeFacilityDistanceRepository, EmployeeFacilityDistanceRepository>();
        services.AddScoped<IContractRepository, ContractRepository>();
        services.AddScoped<IOrderRepository, OrderRepository>();
        services.AddScoped<IAssignmentRepository, AssignmentRepository>();
        services.AddScoped<IAbsenceRepository, AbsenceRepository>();
        services.AddScoped<IAssignmentValidationSettingsRepository, AssignmentValidationSettingsRepository>();
        services.AddScoped<ITimeEntryRepository, TimeEntryRepository>();
        services.AddScoped<IDocumentRepository, DocumentRepository>();
        services.AddScoped<IDocumentStorage, FileSystemDocumentStorage>();
        services.AddScoped<IDocumentUploadPolicy, DocumentUploadPolicy>();
        services.AddScoped<IValueListRepository, ValueListRepository>();
        RegisterValueListUsageCheckers(services);
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IRoleRepository, RoleRepository>();
        services.AddScoped<IRefreshTokenRepository, RefreshTokenRepository>();
        services.AddScoped<IPasswordResetCodeRepository, PasswordResetCodeRepository>();
        services.AddScoped<ILoginAttemptRepository, LoginAttemptRepository>();
        services.AddScoped<ILoginLockoutPolicy, LoginLockoutPolicy>();
        services.AddScoped<IPasswordHasher, PasswordHasher>();
        services.AddScoped<IJwtTokenGenerator, JwtTokenGenerator>();
        services.AddScoped<IRefreshTokenGenerator, RefreshTokenGenerator>();
        services.AddScoped<IPasswordResetCodeGenerator, PasswordResetCodeGenerator>();
        services.AddScoped<IPasswordPolicy, PasswordPolicy>();
        services.AddScoped<IAuditLogRepository, AuditLogRepository>();
        services.AddScoped<IDatabaseConnectivityChecker, DatabaseConnectivityChecker>();

        return services;
    }

    /// <summary>
    /// Eine <see cref="StringFieldValueListUsageChecker"/>-Instanz je String-Feld-Liste (siehe dortige
    /// Doku), plus die FK-basierte Prüfung für "OrderStatus" — Reihenfolge/Vollständigkeit dieser Liste
    /// muss zur Seed-Liste in DbSeeder.SeedValueListsAsync passen.
    /// </summary>
    private static void RegisterValueListUsageCheckers(IServiceCollection services)
    {
        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "EmployeeStatus",
            (db, value, ct) => db.Employees.AsNoTracking().Where(e => e.Status == value)
                .Select(e => new ValueListUsageEntry("Employee", e.Id, $"{e.FirstName} {e.LastName}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "EmploymentType",
            (db, value, ct) => db.Employees.AsNoTracking().Where(e => e.EmploymentType == value)
                .Select(e => new ValueListUsageEntry("Employee", e.Id, $"{e.FirstName} {e.LastName}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "CrmStatus",
            (db, value, ct) => db.Facilities.AsNoTracking().Where(f => f.CrmStatus == value)
                .Select(f => new ValueListUsageEntry("Facility", f.Id, f.Name)).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "FacilityType",
            (db, value, ct) => db.Facilities.AsNoTracking().Where(f => f.FacilityType == value)
                .Select(f => new ValueListUsageEntry("Facility", f.Id, f.Name)).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "ContractType",
            (db, value, ct) => db.Contracts.AsNoTracking().Where(c => c.ContractType == value)
                .Select(c => new ValueListUsageEntry("Contract", c.Id, $"Vertrag {c.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "ContractStatus",
            (db, value, ct) => db.Contracts.AsNoTracking().Where(c => c.Status == value)
                .Select(c => new ValueListUsageEntry("Contract", c.Id, $"Vertrag {c.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker, OrderStatusValueListUsageChecker>();

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "DocumentCategory",
            (db, value, ct) => db.Documents.AsNoTracking().Where(d => d.Category == value)
                .Select(d => new ValueListUsageEntry("Document", d.Id, d.FileName)).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker, QualificationValueListUsageChecker>();

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "ShiftType",
            (db, value, ct) => db.Orders.AsNoTracking().Where(o => o.ShiftType == value)
                .Select(o => new ValueListUsageEntry("Order", o.Id, $"Auftrag {o.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "Priority",
            (db, value, ct) => db.Orders.AsNoTracking().Where(o => o.Priority == value)
                .Select(o => new ValueListUsageEntry("Order", o.Id, $"Auftrag {o.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "BillingInterval",
            (db, value, ct) => db.Facilities.AsNoTracking().Where(f => f.BillingInterval == value)
                .Select(f => new ValueListUsageEntry("Facility", f.Id, f.Name)).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "AbsenceType",
            (db, value, ct) => db.Absences.AsNoTracking().Where(a => a.Type == value)
                .Select(a => new ValueListUsageEntry("Absence", a.Id, $"Antrag {a.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker>(sp => new StringFieldValueListUsageChecker(
            sp.GetRequiredService<OmsorgCoreDbContext>(), "AbsenceStatus",
            (db, value, ct) => db.Absences.AsNoTracking().Where(a => a.Status == value)
                .Select(a => new ValueListUsageEntry("Absence", a.Id, $"Antrag {a.Id}")).ToListAsync(ct)));

        services.AddScoped<IValueListUsageChecker, TimeEntryStatusValueListUsageChecker>();
    }
}
