using ClosedXML.Excel;
using WorkTracker.Domain;
using WorkTracker.Services.Exports;
using Xunit;

namespace WorkTracker.Tests;

public sealed class MonthlyTimesheetExcelExporterTests
{
    [Fact]
    public void Export_ForApril2026EmptyTimesheet_MatchesExpectedWorkbook()
    {
        var exporter = new MonthlyTimesheetExcelExporter();
        var timesheet = CreateTimesheet(new DateOnly(2026, 4, 1), new HashSet<DateOnly>
        {
            new(2026, 4, 6),
            new(2026, 4, 25)
        });
        var templatePath = GetTemplatePath();

        using var templateStream = File.OpenRead(templatePath);
        var workbookBytes = exporter.Export(timesheet, templateStream);

        WorkbookAssert.Equivalent(GetExpectedWorkbookPath(), workbookBytes);
    }

    [Fact]
    public void Export_ForThirtyOneDayMonth_ShiftsTotalColumnAfterLastDay()
    {
        var exporter = new MonthlyTimesheetExcelExporter();
        var timesheet = CreateTimesheet(new DateOnly(2026, 5, 1), new HashSet<DateOnly>());
        var templatePath = GetTemplatePath();

        using var templateStream = File.OpenRead(templatePath);
        var workbookBytes = exporter.Export(timesheet, templateStream);

        using var workbook = new XLWorkbook(new MemoryStream(workbookBytes));
        var worksheet = workbook.Worksheet(1);

        Assert.Equal(31d, worksheet.Cell("AG1").GetDouble());
        Assert.Equal("SUM(C2:AG2)", worksheet.Cell("AH2").FormulaA1);
        Assert.Equal("TOTALE", worksheet.Cell("AH1").GetString());
    }

    private static MonthlyTimesheetModel CreateTimesheet(DateOnly monthStart, ISet<DateOnly> holidays)
    {
        var lastDay = monthStart.AddMonths(1).AddDays(-1);
        var days = new List<MonthlyTimesheetDayModel>();
        for (var date = monthStart; date <= lastDay; date = date.AddDays(1))
        {
            days.Add(new MonthlyTimesheetDayModel
            {
                Date = date,
                IsWeekend = date.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday,
                IsHoliday = holidays.Contains(date)
            });
        }

        return new MonthlyTimesheetModel
        {
            Year = monthStart.Year,
            Month = monthStart.Month,
            Days = days,
            Rows =
            [
                CreateRow("office", days.Count),
                CreateRow("home", days.Count),
                CreateRow("overtime", days.Count),
                CreateRow("weekend", days.Count),
                CreateRow("night", days.Count),
                CreateRow("vacation", days.Count),
                CreateRow("permit", days.Count),
                CreateRow("compensatory-rest", days.Count),
                CreateRow("sick", days.Count),
                CreateRow("holiday", days.Count)
            ]
        };
    }

    private static MonthlyTimesheetRowModel CreateRow(string key, int dayCount)
    {
        return new MonthlyTimesheetRowModel
        {
            Key = key,
            DailyValues = Enumerable.Repeat<decimal?>(null, dayCount).ToList()
        };
    }

    private static string GetExpectedWorkbookPath()
    {
        var repositoryRoot = FindRepositoryRoot();
        var candidate = Path.Combine(repositoryRoot, "tests", "WorkTracker.Tests", "Expected", "monthly-timesheet-2026-04-empty.expected.xlsx");
        return File.Exists(candidate)
            ? candidate
            : GetTemplatePath();
    }

    private static string GetTemplatePath()
    {
        var repositoryRoot = FindRepositoryRoot();
        return Path.Combine(repositoryRoot, "Templates", "monthly-timesheet-template.xlsx");
    }

    private static string FindRepositoryRoot()
    {
        var directory = new DirectoryInfo(AppContext.BaseDirectory);
        while (directory is not null)
        {
            if (File.Exists(Path.Combine(directory.FullName, "WorkTracker.sln")))
            {
                return directory.FullName;
            }

            directory = directory.Parent;
        }

        throw new DirectoryNotFoundException("Unable to locate the WorkTracker repository root.");
    }
}
