using ProjectLibrary.Entities;
using Dapper;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Npgsql;
using ProjectLibrary.Repositores;
using ProjectLibrary.Entites;

namespace ProjectLibrary.Repositories.Implementations
{
    public class LibraryRepository : ILibraryRepository
    {
        private readonly IConnectionString _connectionString;
        private readonly ILogger<LibraryRepository> _logger;

        public LibraryRepository(IConnectionString connectionString, ILogger<LibraryRepository> logger)
        {
            _connectionString = connectionString;
            _logger = logger;
        }

        public void CreateLibrary(Library library)
        {
            _logger.LogInformation("Добавление новой библиотеки");
            _logger.LogDebug("Библиотека: {json}", JsonConvert.SerializeObject(library));

            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                connection.Open(); // открываем соединение
                using var transaction = connection.BeginTransaction(); // начинаем транзакцию
                var queryInsert = @"
                    INSERT INTO Library (Name, Address)
                    VALUES (@Name, @Address);
                    SELECT MAX(Id) FROM LIBRARY";
                var libraryID = connection.QueryFirst<int>(queryInsert, library, transaction); // добавляем в транзакцию запросы, 
                var querySubInsert = @"INSERT INTO BOOK_LIBRARY (BOOKID, LIBRARYID, COUNT)
                                        VALUES (@BOOKID, @LIBRARYID, @COUNT) ";
                foreach (var elem in library.BookLibrary)
                {
                    connection.Execute(querySubInsert, new
                    {
                        elem.BookID,
                        libraryID,
                        elem.Count
                    }, transaction); // добавляем в транзакцию запросы, качество параметра 
                }
                transaction.Commit(); // отправляем транзакцию и она выполняет все положенные в неё запросы
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при добавлении библиотеки");
                throw;
            }
        }

        public void UpdateLibrary(Library library)
        {
            _logger.LogInformation("Редактирование библиотеки");
            _logger.LogDebug("Библиотека: {json}", JsonConvert.SerializeObject(library));

            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                connection.Open();
                using var transaction = connection.BeginTransaction();
                var queryUpdate = @"
                    UPDATE Library
                    SET
                        Name = @Name,
                        Address = @Address
                    WHERE ID = @ID";
                connection.Execute(queryUpdate, library, transaction);
                var querySubUpdate = @"
                    UPDATE BOOK_LIBRARY
                    SET
                        BOOKID = @BOOKID,
                        COUNT = @COUNT
                    WHERE ID = @id";
                var querySubInsert = @"INSERT INTO BOOK_LIBRARY (BookID, LibraryID, Count)
                                        VALUES (@BookID, @Id, @Count) ";
                int ind = 0;
                List<int> listId = connection.Query<int>("Select Id From BOOK_LIBRARY Where LIBRARYID = @id", new { library.Id }).ToList();
                foreach (var elem in library.BookLibrary)
                {
                    if (ind > listId.Count-1)
                    {
                        connection.Execute(querySubInsert, new
                        {
                            elem.BookID,
                            library.Id,
                            elem.Count
                        }, transaction);
                        continue;
                    }
                    int id = listId[ind];
                    connection.Execute(querySubUpdate, new
                    {
                        id,
                        elem.BookID,
                        elem.Count
                    }, transaction);
                    ind++;
                }
                transaction.Commit();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при редактировании библиотеки");
                throw;
            }
        }

        public void DeleteLibrary(int id)
        {
            _logger.LogInformation("Удаление библиотеки");
            _logger.LogDebug("ID библиотеки: {id}", id);

            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                connection.Open();
                using var transaction = connection.BeginTransaction();
                var querySubDelete = @"
                    DELETE FROM Book_Library
                    WHERE LibraryID = @id";
                connection.Execute(querySubDelete, new { id }, transaction);
                var queryDelete = @"
                    DELETE FROM Library
                    WHERE ID = @id";
                connection.Execute(queryDelete, new { id }, transaction);
                transaction.Commit();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при удалении библиотеки");
                throw;
            }
        }

        public Library ReadLibraryById(int id)
        {
            _logger.LogInformation("Получение библиотеки по идентификатору");
            _logger.LogDebug("ID библиотеки: {id}", id);

            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                var querySelect = @"
                    SELECT * FROM Library
                    WHERE ID = @id"; 
                var library = connection.QueryFirstOrDefault<Library>(querySelect, new { id });
                library = Library.CreateEntity(id, library.Name, library.Address, ReadBookLibraryById(id));
                _logger.LogDebug("Найденная библиотека: {json}", JsonConvert.SerializeObject(library));
                return library;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при поиске библиотеки");
                throw;
            }
        }

        public IEnumerable<Library> ReadLibraries()
        {
            _logger.LogInformation("Получение всех библиотек");
            var builder = new QueryBuilder();
            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                var querySelect = @$"SELECT lbr.*, 
                    bl.Bookid as Bookid,
                    bl.count
                    from library lbr
                    left join book_library bl on lbr.id = bl.libraryid {builder.Build()}";
               var BooksFromLibraryDict = new Dictionary<int, List<Book_Library>>();

                var libraries = connection.Query<Library, Book_Library, Library>(querySelect, (libr, bookfromlib) =>
                {
                    if (!BooksFromLibraryDict.TryGetValue(libr.Id, out var booklibrer))
                    {
                        booklibrer = [];
                        BooksFromLibraryDict.Add(libr.Id, booklibrer);
                    }
                    booklibrer.Add(bookfromlib);
                    return libr;
                },
                splitOn: "Bookid", param: new { });
                _logger.LogDebug("Полученные библиотеки: {json}", JsonConvert.SerializeObject(libraries));
                return BooksFromLibraryDict.Select(x =>
                {
                    var lbfb = libraries.First(y => y.Id == x.Key);
                    lbfb.SetLibraryForBook(x.Value);
                    return lbfb;
                }).ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при чтении библиотек");
                throw;
            }
        }

        public IEnumerable<Book_Library> ReadBookLibraryById(int id)
        {
            _logger.LogInformation("Получение всех библиотек");

            try
            {
                using var connection = new NpgsqlConnection(_connectionString.ConnectionString);
                var querySelect = "SELECT * FROM Book_Library WHERE LibraryID = @id";
                var libraries = connection.Query<Book_Library>(querySelect, new { id });
                _logger.LogDebug("Полученные библиотеки: {json}", JsonConvert.SerializeObject(libraries));
                return libraries;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Ошибка при чтении библиотек");
                throw;
            }
        }
    }
}