using System;
using System.IO;
using AsbCloudInfrastructure;
using ClosedXML.Excel;
using Xunit;

namespace AsbCloudWebApi.Tests;

public class XLExtensionsTests
{
   private const string cellUsed = "A1";
   private const string sheetName = "test";

   private readonly IXLWorkbook workbook;

   public XLExtensionsTests()
   {
      workbook = new XLWorkbook();
      workbook.Worksheets.Add(sheetName);
   }

   [Fact]
   public void GetWorksheet_returns_sheet()
   {
      //act
      var sheet = workbook.GetWorksheet(sheetName);

      //assert
      Assert.NotNull(sheet);
   }

   [Theory]
   [MemberData(nameof(valueTypesToSet))]
   public void SetCellValue_returns_success(object value, XLDataType expectedDataType)
   {
      //act
      var cell = GetCell(cellUsed);
      cell.SetCellValue(value);

      //assert
      Assert.Equal(expectedDataType, cell.DataType);
   }
   
   [Fact]
   public void GetCellValue_returns_double()
   {
      //arrange
      const double expectedValue = 2.0d;
      SetCellValue(expectedValue);

      //act
      var actualValue = GetCell(cellUsed).GetCellValue<double>();
      
      //assert
      Assert.Equal(expectedValue, actualValue);
   }

   [Fact]
   public void GetCellValue_returns_float()
   {
      //arrange
      const float expectedValue = 2.0f;
      SetCellValue(expectedValue);
      
      //act
      var actualValue = GetCell(cellUsed).GetCellValue<float>();
      
      //assert
      Assert.Equal(expectedValue, actualValue);
   }

   [Theory]
   [InlineData("test")]
   [InlineData(null)]
   public void GetCellValue_returns_string(string? expectedValue)
   {
      //arrange
      SetCellValue(expectedValue);
      
      //act
      var actualValue = GetCell(cellUsed).GetCellValue<string>();
      
      //assert
      Assert.Equal(expectedValue, actualValue);
   }

   [Fact]
   public void GetCellValue_returns_bool()
   {
      //arrange
      const bool expectedValue = true;
      SetCellValue(expectedValue);
      
      //act
      var actualValue = GetCell(cellUsed).GetCellValue<bool>();
      
      //assert
      Assert.Equal(expectedValue, actualValue);
   }

   [Fact]
   public void GetCellValue_returns_dateTime()
   {
      //arrange
      var expectedValue = DateTime.Parse("2023-01-01");
      SetCellValue(expectedValue);
      
      //act
      var actualValue = GetCell(cellUsed).GetCellValue<DateTime>();
      
      //assert
      Assert.Equal(expectedValue, actualValue);
      Assert.Equal(DateTimeKind.Unspecified, actualValue.Kind);
   }

   [Fact]
   public void GetCellValue_returns_exception()
   {
      //arrange
      SetCellValue("test");

      //assert
      Assert.Throws<FileFormatException>(() => GetCell(cellUsed).GetCellValue<double>());
   }

   [Fact]
   public void GetCellValue_returns_nullable()
   {
      //act
      var actualValue = GetCell(cellUsed).GetCellValue<object?>();
      
      //assert
      Assert.Null(actualValue);
   }

   [Fact]
   public void SetHyperlink_returns_success()
   {
      //arrange
      const string link = "http://test.ru";
      
      //act
      GetCell(cellUsed).SetHyperlink(link);
      
      //assert
      var hyperLink = GetCell(cellUsed).GetHyperlink();
      Assert.NotNull(hyperLink);
   }

   private void SetCellValue<T>(T value)
   {
      var cell = GetCell(cellUsed);
      cell.SetCellValue(value);
   }

   public static readonly object[][] valueTypesToSet =
   {
      new object[] { 2.0d, XLDataType.Number },
      new object[] { 2.0f, XLDataType.Number },
      new object[] { "test", XLDataType.Text },
      new object[] { true, XLDataType.Boolean },
      new object[] { DateTime.UtcNow, XLDataType.DateTime }
   };

   private IXLCell GetCell(string cellAddressInRange)
   {
      var sheet = workbook.GetWorksheet(sheetName);
      return sheet.Cell(cellAddressInRange);
   }
}