// CosmosClient, DatabaseResponse, Database, IndexingPolicy i inne.
using Microsoft.Azure.Cosmos;

using Packt.Shared; // NorthwindContext, Product, Category i inne.
using Microsoft.EntityFrameworkCore; // Dołączenie metod rozszerzeń
using System.Net; // HttpStatusCode.
using Northwind.CosmosDb.Items; // ProductCosmos, CategoryCosmos i inne.

partial class Program
{
  // Właściwości wykorzystywane do obsługi bazy Azure Cosmos DB w lokalnym emulatorze.
  private static string endpointUri = "https://localhost:8081/";
  private static string primaryKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";

  /* 
  // Właściwości wykorzystywane do obsługi bazy Azure Cosmos DB w chmurze.
  private static string account = "apps-services-net7"; // Użyj swojego konta.
  private static string endpointUri = 
    $"https://{account}.documents.azure.com:443/";
  private static string primaryKey = "LGrx7H...gZw=="; // Użyj swojego klucza.
  */

  static async Task CreateCosmosResources()
  {
    SectionTitle("Tworzenie zasobów Azure Cosmos DB");

    try
    {
      using (CosmosClient client = new(
        accountEndpoint: endpointUri,
        authKeyOrResourceToken: primaryKey))
      {
        DatabaseResponse dbResponse = await client
          .CreateDatabaseIfNotExistsAsync(
            "Northwind", throughput: 400 /* RU/s */);

        string status = dbResponse.StatusCode switch
        {
          HttpStatusCode.OK => "istnieje",
          HttpStatusCode.Created => "utworzona",
          _ => "nieznany",
        };

        WriteLine("ID bazy danych: {0}, stan: {1}.",
          arg0: dbResponse.Database.Id, arg1: status);

        IndexingPolicy indexingPolicy = new()
        {
          IndexingMode = IndexingMode.Consistent,
          Automatic = true, // Elementy są indeksowane, chyba że zostaną jawnie wykluczone.
          IncludedPaths = { new IncludedPath { Path = "/*" } }
        };

        ContainerProperties containerProperties = new("Products",
          partitionKeyPath: "/productId")
        {
          IndexingPolicy = indexingPolicy
        };

        ContainerResponse containerResponse = await dbResponse.Database
          .CreateContainerIfNotExistsAsync(
            containerProperties, throughput: 1000 /* RU/s */);

        status = dbResponse.StatusCode switch
        {
          HttpStatusCode.OK => "istnieje",
          HttpStatusCode.Created => "utworzony",
          _ => "nieznany",
        };

        WriteLine("ID kontenera: {0}, stan: {1}.",
          arg0: containerResponse.Container.Id, arg1: status);

        Container container = containerResponse.Container;

        ContainerProperties properties = await container.ReadContainerAsync();
        WriteLine($"  PartitionKeyPath: {properties.PartitionKeyPath}");
        WriteLine($"  LastModified: {properties.LastModified}");
        WriteLine("  IndexingPolicy.IndexingMode: {0}",
          arg0: properties.IndexingPolicy.IndexingMode);
        WriteLine("  IndexingPolicy.IncludedPaths: {0}",
          arg0: string.Join(",", properties.IndexingPolicy
            .IncludedPaths.Select(path => path.Path)));
      }
    }
    catch (HttpRequestException ex)
    {
      WriteLine("Błąd: {0}", arg0: ex.Message);
      WriteLine("Wskazówka: sprawdź czy emulator Azure Cosmos DB jest uruchomiony.");
    }
    catch (Exception ex)
    {
      WriteLine("Błąd: {0}, komunikat: {1}",
        arg0: ex.GetType(),
        arg1: ex.Message);
    }
  }

  static async Task CreateProductItems()
  {
    SectionTitle("Tworzenie elementów produktów");

    double totalCharge = 0.0;

    try
    {
      using (CosmosClient client = new(
        accountEndpoint: endpointUri,
        authKeyOrResourceToken: primaryKey))
      {
        Container container = client.GetContainer(
          databaseId: "Northwind", containerId: "Products");

        using (NorthwindContext db = new())
        {
          ProductCosmos[] products = db.Products

            // Pobranie powiązanych danych do osadzania.
            .Include(p => p.Category)
            .Include(p => p.Supplier)

            // Odfiltrowanie wszystkich produktów z pustą kategorią lub dostawcą,
            // aby uniknąć ostrzeżeń o braku wartości.
            .Where(p => (p.Category != null) && (p.Supplier != null))

            // Rzutowanie jednostek EF Core na typy Cosmos JSON.
            .Select(p => new ProductCosmos
            {
              id = p.ProductId.ToString(),
              productId = p.ProductId.ToString(),
              productName = p.ProductName,
              quantityPerUnit = p.QuantityPerUnit,
              category = new CategoryCosmos
              {
                categoryId = p.Category.CategoryId,
                categoryName = p.Category.CategoryName,
                description = p.Category.Description
              },
              supplier = new SupplierCosmos
              {
                supplierId = p.Supplier.SupplierId,
                companyName = p.Supplier.CompanyName,
                contactName = p.Supplier.ContactName,
                contactTitle = p.Supplier.ContactTitle,
                address = p.Supplier.Address,
                city = p.Supplier.City,
                country = p.Supplier.Country,
                postalCode = p.Supplier.PostalCode,
                region = p.Supplier.Region,
                phone = p.Supplier.Phone,
                fax = p.Supplier.Fax,
                homePage = p.Supplier.HomePage
              },
              unitPrice = p.UnitPrice,
              unitsInStock = p.UnitsInStock,
              reorderLevel = p.ReorderLevel,
              unitsOnOrder = p.UnitsOnOrder,
              discontinued = p.Discontinued,
            })
            .ToArray();

          foreach (ProductCosmos product in products)
          {
            try
            {
              ItemResponse<ProductCosmos> productResponse =
                await container.ReadItemAsync<ProductCosmos>(
                id: product.id, new PartitionKey(product.productId));

              WriteLine("Element o identyfikatorze {0} istnieje. Liczba jednostek RU wykorzystanych przez zapytanie: {1}.",
                productResponse.Resource.id, productResponse.RequestCharge);

              totalCharge += productResponse.RequestCharge;
            }
            catch (CosmosException ex)
              when (ex.StatusCode == HttpStatusCode.NotFound)
            {
              ItemResponse<ProductCosmos> productResponse =
                await container.CreateItemAsync(product);

              WriteLine("Utworzony element o identyfikatorze {0}. Liczba jednostek RU wykorzystanych podczas wstawiania: {1}.",
                productResponse.Resource.id, productResponse.RequestCharge);

              totalCharge += productResponse.RequestCharge;
            }
            catch (Exception ex)
            {
              WriteLine("Błąd: {0}, komunikat: {1}",
                arg0: ex.GetType(),
                arg1: ex.Message);
            }
          }
        }
      }
    }
    catch (HttpRequestException ex)
    {
      WriteLine("Błąd: {0}", arg0: ex.Message);
      WriteLine("Wskazówka: sprawdź czy emulator Azure Cosmos DB jest uruchomiony.");
    }
    catch (Exception ex)
    {
      WriteLine("Błąd: {0}, komunikat: {1}",
        arg0: ex.GetType(),
        arg1: ex.Message);
    }

    WriteLine("Całkowita liczba wykorzystanych jednostek RU: {0:N2}", totalCharge);
  }

  static async Task ListProductItems(string sqlText = "SELECT * FROM c")
  {
    SectionTitle("Lista elementów produktów");

    try
    {
      using (CosmosClient client = new(
        accountEndpoint: endpointUri,
        authKeyOrResourceToken: primaryKey))
      {
        Container container = client.GetContainer(
          databaseId: "Northwind", containerId: "Products");

        WriteLine("Uruchamiane zapytanie: {0}", sqlText);

        QueryDefinition query = new(sqlText);

        using FeedIterator<ProductCosmos> resultsIterator =
          container.GetItemQueryIterator<ProductCosmos>(query);

        if (!resultsIterator.HasMoreResults)
        {
          WriteLine("Brak wyników.");
        }

        while (resultsIterator.HasMoreResults)
        {
          FeedResponse<ProductCosmos> products =
            await resultsIterator.ReadNextAsync();

          WriteLine("Kod stanu: {0}, liczba wykorzystanych jednostek RU: {1}.",
            products.StatusCode, products.RequestCharge);

          WriteLine("Liczba znalezionych produktów: {0}.", arg0: products.Count);

          foreach (ProductCosmos product in products)
          {
            WriteLine("id: {0}, productName: {1}, unitPrice: {2}",
              arg0: product.id, arg1: product.productName,
              arg2: product.unitPrice);
          }
        }
      }
    }
    catch (HttpRequestException ex)
    {
      WriteLine("Błąd: {0}", arg0: ex.Message);
      WriteLine("Wskazówka: sprawdź czy emulator Azure Cosmos DB jest uruchomiony.");
    }
    catch (Exception ex)
    {
      WriteLine("Błąd: {0}, komunikat: {1}",
        arg0: ex.GetType(),
        arg1: ex.Message);
    }
  }

  static async Task DeleteProductItems()
  {
    SectionTitle("Usuwanie elementów produktów");

    double totalCharge = 0.0;

    try
    {
      using (CosmosClient client = new(
        accountEndpoint: endpointUri,
        authKeyOrResourceToken: primaryKey))
      {
        Container container = client.GetContainer(
          databaseId: "Northwind", containerId: "Products");

        string sqlText = "SELECT * FROM c";

        WriteLine("Uruchamiane zapytanie: {0}", sqlText);

        QueryDefinition query = new(sqlText);

        using FeedIterator<ProductCosmos> resultsIterator =
          container.GetItemQueryIterator<ProductCosmos>(query);

        while (resultsIterator.HasMoreResults)
        {
          FeedResponse<ProductCosmos> products =
            await resultsIterator.ReadNextAsync();

          foreach (ProductCosmos product in products)
          {
            WriteLine("Usuwanie elementu, id: {0}, productName: {1}",
              arg0: product.id, arg1: product.productName);

            ItemResponse<ProductCosmos> response =
              await container.DeleteItemAsync<ProductCosmos>(
              id: product.id, partitionKey: new(product.id));

            WriteLine("Kod stanu: {0}, liczba wykorzystanych jednostek RU: {1}.",
              response.StatusCode, response.RequestCharge);

            totalCharge += response.RequestCharge;
          }
        }
      }
    }
    catch (HttpRequestException ex)
    {
      WriteLine("Błąd: {0}", arg0: ex.Message);
      WriteLine("Wskazówka: sprawdź czy emulator Azure Cosmos DB jest uruchomiony.");
    }
    catch (Exception ex)
    {
      WriteLine("Błąd: {0}, komunikat: {1}",
        arg0: ex.GetType(),
        arg1: ex.Message);
    }

    WriteLine("Całkowita liczba wykorzystanych jednostek RU: {0:N2}", totalCharge);
  }
}
