using System.ComponentModel;
using Evos.Api.Repository;
using Evos.Api.Samples.Infrastructure;
using Evos.Core.Container.ProjectScope;
using Evos.ModelsRepository.BasicData;
using Evos.ModelsRepository.DataProvider.TransactionDescriptions;
using Evos.ModelsRepository.Products;
using Evos.ModelsRepository.SpaceModel;

namespace Evos.Api.Samples
{
    /// Низкоуровневое API: создание репозитория, запись в транзакции, повторное открытие с диска.
    /// Репозиторий живёт в папке — путь к нему задаёт ProjectLocation (директория + имя файла проекта).
    [Localizable(false)]
    public static class RepositoryLifecycleSample
    {
        public static void Run()
        {
            var directory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            var projectName = Path.GetRandomFileName();

            try
            {
                var location = new ProjectLocation(directory, projectName);

                CreateRepositoryWithProducts(location);

                // Репозиторий закрыт, данные лежат на диске. Открываем заново — это отдельный вызов фабрики.
                var names = ReadProductNames(location);

                Console.WriteLine($"После переоткрытия в репозитории продуктов: {names.Count}");
                foreach (var name in names)
                {
                    Console.WriteLine($"  {name}");
                }

                // Порядок обхода GetProducts() репозиторий не обещает — сверяем состав, а не последовательность.
                SampleCheck.That(
                    names.Count == 2 && names.Contains("Насос") && names.Contains("Клапан"),
                    "продукты должны пережить закрытие репозитория");
            }
            finally
            {
                Directory.Delete(directory, true);
            }
        }

        /// Новый репозиторий создаётся OpenNewRepository. Любая запись идёт только внутри транзакции:
        /// TryExecuteTransaction сам открывает write-соединение и коммитит его при выходе.
        private static void CreateRepositoryWithProducts(IProjectLocation location)
        {
            using var repository = new ApiConcurrentRepositoryFactory().OpenNewRepository(location);

            repository.TryExecuteTransaction(
                writeAccess =>
                {
                    var pump = writeAccess.NewNamedProduct("Насос", ProductKind.Designed);
                    var valve = writeAccess.NewNamedProduct("Клапан", ProductKind.Designed);

                    // Экземпляр — вхождение одного продукта в другой.
                    pump.NewProductInstance(valve);
                },
                new TransactionInfo(TransactionDescription.AddingProduct, TransactionMode.Default));
        }

        /// Существующий репозиторий открывается OpenRepository. Чтение — через read-соединение,
        /// оно не блокирует запись и его надо освобождать.
        private static List<string> ReadProductNames(IProjectLocation location)
        {
            using var repository = new ApiConcurrentRepositoryFactory().OpenRepository(location);
            using var readConnection = repository.ReadConnection();

            return readConnection.GetProducts()
                .Select(product => product.GetProductName())
                .ToList();
        }
    }
}
