using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RackPeek.Domain.Git;
using RackPeek.Domain.Persistence;
using RackPeek.Domain.Resources;
using RackPeek.Domain.Resources.Connections;
using RackPeek.Domain.Resources.Hardware;
using RackPeek.Domain.Resources.Services;
using RackPeek.Domain.Resources.SystemResources;
using RackPeek.Domain.UseCases;
using RackPeek.Domain.UseCases.Cpus;
using RackPeek.Domain.UseCases.Drives;
using RackPeek.Domain.UseCases.Gpus;
using RackPeek.Domain.UseCases.Labels;
using RackPeek.Domain.UseCases.Ports;
using RackPeek.Domain.UseCases.Tags;

namespace RackPeek.Domain;

public interface IResourceUseCase<T> where T : Resource {
}

public static class ServiceCollectionExtensions {

    public static IServiceCollection AddGitServices(
        this IServiceCollection services,
        IConfiguration config,
        string? yamlPath = null) {
        var gitToken = config["GIT_TOKEN"];
        if (!string.IsNullOrWhiteSpace(gitToken) && !string.IsNullOrWhiteSpace(yamlPath)) {
            var gitUsername = config["GIT_USERNAME"] ?? "git";
            var insecureTls = string.Equals(
                config["GIT_INSECURE_TLS"], "true", StringComparison.OrdinalIgnoreCase);

            services.AddSingleton<IGitCredentialsProvider>(
                _ => new TokenCredentialsProvider(gitUsername, gitToken));

            services.AddSingleton<IGitRepository>(sp => {
                IGitCredentialsProvider creds = sp.GetRequiredService<IGitCredentialsProvider>();
                return new LibGit2GitRepository(yamlPath, creds, insecureTls);
            });
            RpkConstants.HasGitServices = true;
        }
        else {
            RpkConstants.HasGitServices = false;
            services.AddSingleton<IGitRepository, NullGitRepository>();
        }

        return services;
    }
    public static IServiceCollection AddResourceUseCases(
        this IServiceCollection services,
        Assembly assembly) {
        IEnumerable<Type> types = assembly.GetTypes()
            .Where(t => !t.IsAbstract && !t.IsInterface);

        foreach (Type type in types) {
            IEnumerable<Type> resourceUseCaseInterfaces = type.GetInterfaces()
                .Where(i =>
                    i.IsGenericType &&
                    i.GetInterfaces().Any(parent =>
                        parent.IsGenericType &&
                        parent.GetGenericTypeDefinition() == typeof(IResourceUseCase<>)));

            foreach (Type serviceType in resourceUseCaseInterfaces) services.AddScoped(serviceType, type);
        }

        return services;
    }


    public static IServiceCollection AddUseCases(
        this IServiceCollection services) {
        services.AddScoped(typeof(IAddResourceUseCase<>), typeof(AddResourceUseCase<>));
        services.AddScoped(typeof(IAddLabelUseCase<>), typeof(AddLabelUseCase<>));
        services.AddScoped(typeof(IAddTagUseCase<>), typeof(AddTagUseCase<>));
        services.AddScoped(typeof(ICloneResourceUseCase<>), typeof(CloneResourceUseCase<>));
        services.AddScoped(typeof(IDeleteResourceUseCase<>), typeof(DeleteResourceUseCase<>));
        services.AddScoped(typeof(IRemoveLabelUseCase<>), typeof(RemoveLabelUseCase<>));
        services.AddScoped(typeof(IRemoveTagUseCase<>), typeof(RemoveTagUseCase<>));
        services.AddScoped(typeof(IGetAllResourcesByKindUseCase<>), typeof(GetAllResourcesByKindUseCase<>));
        services.AddScoped(typeof(IGetResourceByNameUseCase<>), typeof(GetResourceByNameUseCase<>));
        services.AddScoped(typeof(IRenameResourceUseCase<>), typeof(RenameResourceUseCase<>));

        services.AddScoped(typeof(IAddCpuUseCase<>), typeof(AddCpuUseCase<>));
        services.AddScoped(typeof(IRemoveCpuUseCase<>), typeof(RemoveCpuUseCase<>));
        services.AddScoped(typeof(IUpdateCpuUseCase<>), typeof(UpdateCpuUseCase<>));


        services.AddScoped(typeof(IAddDriveUseCase<>), typeof(AddDriveUseCase<>));
        services.AddScoped(typeof(IRemoveDriveUseCase<>), typeof(RemoveDriveUseCase<>));
        services.AddScoped(typeof(IUpdateDriveUseCase<>), typeof(UpdateDriveUseCase<>));

        services.AddScoped(typeof(IAddGpuUseCase<>), typeof(AddGpuUseCase<>));
        services.AddScoped(typeof(IRemoveGpuUseCase<>), typeof(RemoveGpuUseCase<>));
        services.AddScoped(typeof(IUpdateGpuUseCase<>), typeof(UpdateGpuUseCase<>));

        services.AddScoped(typeof(IAddPortUseCase<>), typeof(AddPortUseCase<>));
        services.AddScoped(typeof(IRemovePortUseCase<>), typeof(RemovePortUseCase<>));
        services.AddScoped(typeof(IUpdatePortUseCase<>), typeof(UpdatePortUseCase<>));

        services.AddScoped(typeof(IAddConnectionUseCase), typeof(AddConnectionUseCase));
        services.AddScoped(typeof(IGetConnectionForPortUseCase), typeof(GetConnectionForPortUseCase));
        services.AddScoped(typeof(IGetConnectionsForResourceUseCase), typeof(GetConnectionsForResourceUseCase));
        services.AddScoped(typeof(IRemoveConnectionUseCase), typeof(RemoveConnectionUseCase));


        IEnumerable<Type>? usecases = Assembly.GetAssembly(typeof(IUseCase))
            ?.GetTypes()
            .Where(t =>
                !t.IsAbstract &&
                typeof(IUseCase).IsAssignableFrom(t)
            );

        if (usecases != null)
            foreach (Type type in usecases)
                services.AddScoped(type);

        return services;
    }

    public static IServiceCollection AddYamlRepos(
        this IServiceCollection services) {
        services.AddScoped<IHardwareRepository, YamlHardwareRepository>();
        services.AddScoped<ISystemRepository, YamlSystemRepository>();
        services.AddScoped<IServiceRepository, ServiceRepository>();
        return services;
    }
}
