using System.Data;
using FastBlog.Core.Abstractions.Repositories.Blogs;
using FastBlog.Core.Abstractions.Repositories.Files;
using FastBlog.Core.Abstractions.Repositories.Users;
using FastBlog.Core.Db;
using FastBlog.Core.Models;
using FastBlog.Core.Models.Blogs;
using FastBlog.Core.Models.Users;
using FastBlog.Core.Options;
using FastBlog.Core.Repositories;
using FastBlog.Core.Services;
using FluentMigrator.Runner;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace FastBlog.Core;

public static class DependencyInjection
{
    public static IServiceCollection AddCore(this IServiceCollection services, IConfiguration configuration)
    {
        // application
        services.AddTransient<BlogService>();
        services.AddTransient<FileService>();
        services.AddTransient<UserService>();
        services.AddTransient<SessionService>();
        
        // options
        services.Configure<FileStoreOptions>(configuration.GetSection("FileStore"));
        services.Configure<SessionOptions>(configuration.GetSection("Sessions"));
        
        // infrastructure
        services.AddTransient<IBlogMetaRepository, BlogMetaRepository>();
        services.AddTransient<IBlogFileRepository, BlogFileRepository>();
        services.AddTransient<IFileMetaRepository, FileMetaRepository>();
        services.AddTransient<IFileRepository, FileRepository>();
        services.AddTransient<ISessionRepository,SessionRepository>();
        services.AddTransient<IUserRepository, UserRepository>();

        // storage
        var connectionString = configuration.GetConnectionString("Sqlite");
        
        services.AddScoped<IDbConnection>(_ => new SqliteConnection());
        services.AddTransient<SqliteConnection>(_ => new(connectionString));
        services.AddSingleton<SqliteConnectionFactory>(_ => new(configuration));
        
        services.AddFluentMigratorCore()
            .ConfigureRunner(rb =>
                rb.AddSQLite()
                    .WithGlobalConnectionString(connectionString)
                    .ScanIn(typeof(Db.Migrations.Init).Assembly).For.Migrations())
            .AddLogging(lb => lb.AddFluentMigratorConsole());
        
        return services;
    }

    public static async Task MigrateUp(this IServiceProvider provider, IConfiguration configuration)
    {
        await using var scope = provider.CreateAsyncScope();
        var runner = scope.ServiceProvider.GetRequiredService<IMigrationRunner>();
        runner.MigrateUp();
        await TryAddMainPage(scope.ServiceProvider);
        await TryAddDefaultUser(scope.ServiceProvider);
    }

    private static async Task TryAddMainPage(IServiceProvider provider)
    {
        var blogService = provider.GetRequiredService<BlogService>();
        var mainPage = await blogService.Get(null);
        if (mainPage is not null)
            return;
        
        await blogService.Update(new Blog
        {
            Text = BlogDefaultBody,
            Metadata = new BlogMeta
            {
                Title = "Welcome",
                Slug = null,
                CreatedAt = DateTime.UtcNow,
                ModifiedAt = DateTime.UtcNow,
                SourceLocation = "welcome.md",
                ShowDetails = false,
                Visible = false
            }
        });
    }

    private static async Task TryAddDefaultUser(this IServiceProvider provider)
    {
        var userService = provider.GetRequiredService<UserService>();
        var existingUserResult = await userService.GetUsers(new PagedRequest(1, 0));
        if (existingUserResult.Amount >= 1)
            return;

        var newUser = new NewUser
        {
            Username = "admin",
            Password = "admin"
        };

        var addResult = await userService.CreateUser(newUser);
        if (addResult is null)
            throw new ApplicationException("Failed to create new user!");
    }
    
    private const string BlogDefaultBody = """
                                           # Index Page  
                                           ---  
                                           Welcome to your first blog
                                           """;
}