using Couchbase.Lite;
using Microsoft.Extensions.Options;
using WorkTracker.Configuration;

namespace WorkTracker.Services.Storage;

public sealed class CouchbaseLiteDatabaseProvider : IDisposable
{
    private const string AppSettingsCollectionName = "app_settings";
    private const string SystemCollectionName = "system";
    private const string UsersCollectionName = "users";
    private const string WorkDaysCollectionName = "workdays";
    private const string SchemaVersionDocumentId = "database_schema";

    public const int CurrentDatabaseSchemaVersion = 1;

    private readonly Database database;
    private readonly IReadOnlyDictionary<string, Collection> backupCollections;

    public CouchbaseLiteDatabaseProvider(IOptions<CouchbaseLiteOptions> options, IHostEnvironment environment)
    {
        var configuredOptions = options.Value;
        var databaseDirectory = ResolveDirectory(configuredOptions.Directory, environment.ContentRootPath);
        Directory.CreateDirectory(databaseDirectory);

        database = new Database(
            configuredOptions.DatabaseName,
            new DatabaseConfiguration
            {
                Directory = databaseDirectory
            });

        System = database.GetCollection(SystemCollectionName) ?? database.CreateCollection(SystemCollectionName);
        AppSettings = database.GetCollection(AppSettingsCollectionName) ?? database.CreateCollection(AppSettingsCollectionName);
        Users = database.GetCollection(UsersCollectionName) ?? database.CreateCollection(UsersCollectionName);
        WorkDays = database.GetCollection(WorkDaysCollectionName) ?? database.CreateCollection(WorkDaysCollectionName);

        backupCollections = new Dictionary<string, Collection>(StringComparer.Ordinal)
        {
            [AppSettingsCollectionName] = AppSettings,
            [UsersCollectionName] = Users,
            [WorkDaysCollectionName] = WorkDays
        };

        DatabaseSchemaVersion = EnsureDatabaseSchemaVersion();
    }

    public Collection System { get; }

    public Collection AppSettings { get; }

    public Collection Users { get; }

    public Collection WorkDays { get; }

    public int DatabaseSchemaVersion { get; }

    public IReadOnlyDictionary<string, Collection> BackupCollections => backupCollections;

    public void ExecuteInBatch(Action action)
    {
        ArgumentNullException.ThrowIfNull(action);
        database.InBatch(action);
    }

    public void Dispose()
    {
        database.Close();
        database.Dispose();
    }

    private static string ResolveDirectory(string configuredDirectory, string contentRootPath)
    {
        if (string.IsNullOrWhiteSpace(configuredDirectory))
        {
            return Path.Combine(contentRootPath, "App_Data", "couchbase");
        }

        return Path.IsPathRooted(configuredDirectory)
            ? configuredDirectory
            : Path.GetFullPath(Path.Combine(contentRootPath, configuredDirectory));
    }

    private int EnsureDatabaseSchemaVersion()
    {
        var document = System.GetDocument(SchemaVersionDocumentId);
        if (document is not null && document.Contains("schemaVersion"))
        {
            return document.GetInt("schemaVersion");
        }

        var now = DateTimeOffset.UtcNow;
        var mutableDocument = new MutableDocument(SchemaVersionDocumentId);
        mutableDocument.SetInt("schemaVersion", CurrentDatabaseSchemaVersion);
        mutableDocument.SetString("createdAtUtc", now.ToString("O"));
        mutableDocument.SetString("updatedAtUtc", now.ToString("O"));
        System.Save(mutableDocument);
        return CurrentDatabaseSchemaVersion;
    }
}