﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LightningDB;

namespace beacon.service.Security
{
    /// <summary>
    /// <see cref="IAuthStore"/> implemented ad LigntningDB.
    /// </summary>
    public sealed class AuthStore : IAuthStore, IDisposable
    {
        private readonly LightningEnvironment _environment;

        /// <param name="storePath">Path to database.</param>
        public AuthStore(string storePath)
        {
            _environment = new LightningEnvironment(storePath) { MaxDatabases = 2 };
            _environment.Open();
            using (var transaction = _environment.BeginTransaction())
            {
                using (
                    transaction.OpenDatabase("users",
                        new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create }))
                {
                    transaction.Commit();
                }
            }
        }

        /// <inheritdoc />
        public bool Check(string user, byte[] hash)
        {
            using (var transaction = _environment.BeginTransaction(TransactionBeginFlags.ReadOnly))
            {
                using (
                    var db = transaction.OpenDatabase("users"))
                {
                    var stored = transaction.Get(db, Encoding.UTF8.GetBytes(user.ToLower()));
                    if (stored == null)
                        return false;
                    return stored.SequenceEqual(hash);
                }
            }
        }

        /// <inheritdoc />
        public void Upsert(string user, byte[] hash)
        {
            using (var transaction = _environment.BeginTransaction())
            {
                using (
                    var db = transaction.OpenDatabase("users",
                        new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create }))
                {
                    transaction.Put(db, Encoding.UTF8.GetBytes(user.ToLower()), hash);
                    transaction.Commit();
                }
            }
        }

        /// <inheritdoc />
        public void Delete(string user)
        {
            using (var transaction = _environment.BeginTransaction())
            {
                using (
                    var db = transaction.OpenDatabase("users",
                        new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create }))
                {
                    transaction.Delete(db, Encoding.UTF8.GetBytes(user.ToLower()));
                    transaction.Commit();
                }
            }
        }

        /// <inheritdoc />
        public List<string> GetAll()
        {
            var result = new List<string>();
            using (var transaction = _environment.BeginTransaction(TransactionBeginFlags.ReadOnly))
            {
                using (
                    var db = transaction.OpenDatabase("users"))
                {
                    using (var cursor = transaction.CreateCursor(db))
                    {
                        while (cursor.MoveNext())
                        {
                            result.Add(Encoding.UTF8.GetString(cursor.Current.Key));
                        }
                    }
                }
            }
            return result;
        }

        /// <inheritdoc />
        public bool Exists(string user)
        {
            using (var transaction = _environment.BeginTransaction(TransactionBeginFlags.ReadOnly))
            {
                using (
                    var db = transaction.OpenDatabase("users"))
                {
                    return transaction.ContainsKey(db, Encoding.UTF8.GetBytes(user.ToLower()));
                }
            }
        }

        /// <inheritdoc />
        public void Dispose()
        {
            _environment?.Dispose();
        }
    }
}
