using Microsoft.AspNetCore.Authorization; // [Authorize]
using Microsoft.AspNetCore.Mvc; // Controller, IActionResult, [ResponseCache]
using Northwind.Mvc.Models; // ErrorViewModel
using System.Diagnostics; // Activity
using BibliotekaWspolna; // NorthwindContext
using Microsoft.EntityFrameworkCore; // Include extension method
using Northwind.Wspolne; // WeatherForecast
using System.Text; // Encoding
using Grpc.Net.Client; // GrpcChannel

namespace Northwind.Mvc.Controllers;
public class HomeController : Controller
{
  private readonly ILogger<HomeController> _logger;
  private readonly NorthwindContext db;
  private readonly IHttpClientFactory fabrykaKlientow;

  public HomeController(ILogger<HomeController> logger,
    NorthwindContext wstrzyknietyKontekst,
    IHttpClientFactory httpClientFactory)
  {
    _logger = logger;
    db = wstrzyknietyKontekst;
    fabrykaKlientow = httpClientFactory;
  }

  [ResponseCache(Duration = 10, Location = ResponseCacheLocation.Any)]
  public async Task<IActionResult> Index()
  {
    _logger.LogError("Naprawdę poważny błąd (tylko żartuję!)");
    _logger.LogWarning("To jest pierwsze ostrzeżenie!");
    _logger.LogWarning("Drugie ostrzeżenie!");
    _logger.LogInformation("Jestem metodą Index kontrolera HomeController.");
    
    try
    {
      HttpClient klient = fabrykaKlientow.CreateClient(
        name: "Minimalne.WebApi");

      HttpRequestMessage zadanie = new(
        method: HttpMethod.Get, requestUri: "api/pogoda");

      HttpResponseMessage odpowiedz = await klient.SendAsync(zadanie);

      ViewData["pogoda"] = await odpowiedz.Content
        .ReadFromJsonAsync<Pogodynka[]>();
    }
    catch (Exception ex)
    {
      _logger.LogWarning($"Seriws Minimalne.WebApi nie odpowiada. Wyjątek: {ex.Message}");
      ViewData["weather"] = Enumerable.Empty<Pogodynka>().ToArray();
    }

    HomeIndexViewModel model = new
    (
      LiczbaOdwiedzin: (new Random()).Next(1, 1001),
      Kategorie: await db.Categories.ToListAsync(),
      Produkty: await db.Products.ToListAsync()
    );
    return View(model); // przekazanie modelu do widoku
  }

  [Route("prywatnosc")]
  [Authorize(Roles = "Administratorzy")]
  public IActionResult Privacy()
  {
    return View();
  }

  [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
  public IActionResult Error()
  {
    return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
  }

  public async Task<IActionResult> DaneProduktu(int? id)
  {
    if (!id.HasValue)
    {
      return BadRequest("W ścieżce musisz podać ID produktu, na przykład: /Home/DaneProduktu/21");
    }

    Product? model = await db.Products
      .SingleOrDefaultAsync(p => p.ProductId == id);

    if (model == null)
    {
      return NotFound($"Nie znaleziono produku od identyfikatorze {id}.");
    }

    return View(model); // przekazanie modelu do widoku i zwrócenie wyniku
  }

  public IActionResult WiazanieModelu()
  {
    return View(); // strona z formularzem
  }

  [HttpPost]
  public IActionResult WiazanieModelu(Rzecz rzecz)
  {
    HomeWiazanieModeluViewModel model = new(
      rzecz,
      !ModelState.IsValid,
      ModelState.Values
        .SelectMany(stan => stan.Errors)
        .Select(blad => blad.ErrorMessage)
    );
    return View(model);
  }

  public IActionResult ProduktyDrozszeNiz(decimal? cena)
  {
    if (!cena.HasValue)
    {
      return BadRequest("Cenę produktu musisz podać w ramach zapytania, na przykład: /Home/ProduktyDrozszeNiz?cena=50");
    }

    IEnumerable<Product> model = db.Products
      .Include(p => p.Category)
      .Include(p => p.Supplier)
      .Where(p => p.UnitPrice > cena);

    if (!model.Any())
    {
      return NotFound(
        $"Nie ma produktów droższych niż {cena:C}.");
    }

    ViewData["CenaMaksymalna"] = cena.Value.ToString("C");
    return View(model); // przekazanie modelu do widoku
  }

  public async Task<IActionResult> Klienci(string kraj)
  {
    string uri;

    if (string.IsNullOrEmpty(kraj))
    {
      ViewData["Title"] = "Klienci z całego świada";
      uri = "api/klienci/";
    }
    else
    {
      ViewData["Title"] = $"Klienci z {kraj}";
      uri = $"api/klienci/?kraj={kraj}";
    }

    HttpClient klient = fabrykaKlientow.CreateClient(
      name: "Northwind.WebApi");

    HttpRequestMessage zadanie = new(
      method: HttpMethod.Get, requestUri: uri);

    HttpResponseMessage odpowiedz = await klient.SendAsync(zadanie);

    IEnumerable<Customer>? model = await odpowiedz.Content
      .ReadFromJsonAsync<IEnumerable<Customer>>();

    return View(model);
  }

  public async Task<IActionResult> Uslugi()
  {
    try
    {
      HttpClient klient = fabrykaKlientow.CreateClient(
        name: "Northwind.OData");

      HttpRequestMessage zadanie = new(
        method: HttpMethod.Get, requestUri:
        "catalog/produkty/?$filter=startswith(ProductName, 'Cha')&$select=ProductId,ProductName,UnitPrice");

      HttpResponseMessage odpowiedz = await klient.SendAsync(zadanie);

      ViewData["productsCha"] = (await odpowiedz.Content
        .ReadFromJsonAsync<ODataProducts>())?.Value;
    }
    catch (Exception ex)
    {
      _logger.LogWarning($"Wyjątek z serwisu Northwind.OData: {ex.Message}");
    }

    try
    {
      HttpClient klient = fabrykaKlientow.CreateClient(
        name: "Northwind.GraphQL");

      HttpRequestMessage zadanie = new(
        method: HttpMethod.Post, requestUri: "graphql");

      zadanie.Content = new StringContent(content: @"
        query {
          products (categoryId: 8) {
            productId
            productName
            unitsInStock
          }
        }",
        encoding: Encoding.UTF8,
        mediaType: "application/graphql");

      HttpResponseMessage odpowiedz = await klient.SendAsync(zadanie);

      if (odpowiedz.IsSuccessStatusCode)
      {
        ViewData["seafoodProducts"] = (await odpowiedz.Content
          .ReadFromJsonAsync<GraphQLProducts>())?.Data?.Products;
      }
      else
      {
        ViewData["seafoodProducts"] = Enumerable.Empty<Product>().ToArray();
      }
    }
    catch (Exception ex)
    {
      _logger.LogWarning($"Wyjątek z serwisu Northwind.GraphQL: {ex.Message}");
    }

    try
    {
      using (GrpcChannel kanal =
        GrpcChannel.ForAddress("https://localhost:5006"))
      {
        Greeter.GreeterClient pozdrowienie = new(kanal);
        HelloReply reply = await pozdrowienie.SayHelloAsync(
          new HelloRequest { Name = "Henrietta" });
        ViewData["pozdrowienie"] = "Pozdrowienia z serwisu gRPC: " + reply.Message;
      }
    }
    catch (Exception)
    {
      _logger.LogWarning($"Serwis Northwind.gRPC nie odpowiada.");
    }

    try
    {
      using (GrpcChannel kanal =
        GrpcChannel.ForAddress("https://localhost:5006"))
      {
        Shipr.ShiprClient dostawca = new(kanal);

        ShipperReply odpowiedz = await dostawca.GetShipperAsync(
          new ShipperRequest { ShipperId = 3 });

        ViewData["shipr"] = new Shipper
        {
          ShipperId = odpowiedz.ShipperId,
          CompanyName = odpowiedz.CompanyName,
          Phone = odpowiedz.Phone
        };
      }
    }
    catch (Exception)
    {
      _logger.LogWarning($"Serwis Northwind.gRPC nie odpowiada.");
    }

    return View();
  }

  public IActionResult Chat()
  {
    return View();
  }

}
