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

namespace AsbCloudInfrastructure.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);
    }
}