using Microsoft.Extensions.Logging;
using ReportingServices.Shared.Models.ProductionReport;
using ReportingServices.Shared.ViewModels.ProductionReport;

namespace ReportingServices.Shared.HelperClasses;

public static class DailyReportHelper
{
    public static DailyReport SetUpDailyReport(ILogger logger, Dictionary<string, string> filePaths, string baseUrlScrapeDb)
    {
        DailyReport report = new();
        DateTime currentDateTime = DateTime.Now;

        try
        {
            List<SLLTool> tools = JsonFileHandler.LoadJSONFile<List<SLLTool>>(filePaths["SLL"]);
            ManualReportEntries manualEntries = JsonFileHandler.LoadJSONFile<ManualReportEntries>(filePaths["DailyReport"]);

            if (currentDateTime.DayOfWeek == DayOfWeek.Monday && tools[^1].Date == currentDateTime.Date.AddDays(-1))
                report.SLLTools = new List<SLLTool>();
            else
                report.SLLTools = tools;

            report.ManualReportEntries = manualEntries;

            report.ToolStatesByOwner = JsonFileHandler.LoadJSONFile<Dictionary<string, List<string>>>(filePaths["ToolStateOwners"]);

        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Failed to load JsonFiles.");
            logger.LogInformation("SLL File Path: {path}", filePaths["SLL"]);
            logger.LogInformation("Manual Report Entries File Path: {path}", filePaths["DailyReport"]);
        }

        List<string> cleanTools = new()
        {
            "AHPS",
            "AKRION1",
            "CB3",
            "MES",
            "SRD 1",
            "SRD 2"
        };

        List<string> metrologyTools = new()
        {
            "ASET",
            "BIORAD2",
            "BIORAD3",
            "BIORAD4",
            "BIORAD5",
            "CDE2",
            "CDE3",
            "CDE5",
            "FLEXUS",
            "HGCV1",
            "HGCV2",
            "HGCV3",
            "SRP"
        };

        List<Reactor> reactors = new();
        Dictionary<string, Task<int>> lastUpTransactions = new();
        List<Task<List<ReactorEvent>>> toolEvents = new();
        List<Task<ToolEvent>> cleanEvents = new();
        List<Task<ToolEvent>> metrologyEvents = new();

        Task<YieldInformation> task1 = null;
        Task<YieldInformation> task2 = null;
        Task<QuarterlyTargets> targets = null;
        Task<List<RDS>> rds = null;
        Task<DateTime> task3 = null;
        Task<OutsAndScrapTotal> task4 = null;
        Task<OutsAndScrapTotal> task5 = null;
        Task<OutsAndScrapTotal> task6 = null;
        Task<List<string>> task7 = null;

        try
        {
            reactors = ApiCaller.GetApi<List<Reactor>>(baseUrlScrapeDb + "Reactors").Result;

            foreach (Reactor reactor in reactors)
            {
                toolEvents.Add(ApiCaller.GetApi<List<ReactorEvent>>(baseUrlScrapeDb + "ReactorEvents?startDate=" + report.StartDate.ToString() +
                    "&endDate=" + currentDateTime.ToString() + "&reactorNumber=" + reactor.ReactorNumber + "&reactorType=" + reactor.Type));

                lastUpTransactions.Add(reactor.ReactorNumber.ToString(),
                    ApiCaller.GetApi<int>(baseUrlScrapeDb + "GetLastUpTransaction?reactorNumber=" + reactor.ReactorNumber));
            }

            foreach (string tool in cleanTools)
            {
                cleanEvents.Add(ApiCaller.GetApi<ToolEvent>(baseUrlScrapeDb + "ToolEvents?toolID=" + tool));
            }

            foreach (string tool in metrologyTools)
            {
                metrologyEvents.Add(ApiCaller.GetApi<ToolEvent>(baseUrlScrapeDb + "ToolEvents?toolID=" + tool));
            }

            task1 = ApiCaller.GetApi<YieldInformation>(baseUrlScrapeDb + "ReactorOuts?startDate=" + report.StartDate.ToString() + "&endDate=" + currentDateTime.ToString());
            task2 = ApiCaller.GetApi<YieldInformation>(baseUrlScrapeDb + "ReactorOuts?startDate=" + report.StartDate.AddDays(-7).ToString() + "&endDate=" + report.StartDate.ToString());
            task3 = ApiCaller.GetApi<DateTime>(baseUrlScrapeDb + "GetQuarterStartDate");
            targets = ApiCaller.GetApi<QuarterlyTargets>(baseUrlScrapeDb + "Targets");

            rds = ApiCaller.GetApi<List<RDS>>(baseUrlScrapeDb + "RDS?date=" + report.StartDate.ToString());
            task4 = ApiCaller.GetApi<OutsAndScrapTotal>(baseUrlScrapeDb + "GetOutsAndScrapTotals?startDate=" + task3.Result + "&endDate=" + currentDateTime.ToString());
            task5 = ApiCaller.GetApi<OutsAndScrapTotal>(baseUrlScrapeDb + "GetOutsAndScrapTotals?startDate=" + task3.Result + "&endDate=" + report.StartDate.ToString());
            task6 = ApiCaller.GetApi<OutsAndScrapTotal>(baseUrlScrapeDb + "GetOutsAndScrapTotals?startDate=" + task3.Result + "&endDate=" + report.StartDate.AddDays(-7).ToString());
            task7 = ApiCaller.GetApi<List<string>>(baseUrlScrapeDb + "GetCurrentHotWORunning");
        }
        catch (Exception ex)
        {
            logger.LogCritical(ex, "Failed to send get requests to scrapedb endpoints.");
        }

        try
        {
            report.QuarterlyTargets = targets.Result;
            report.SetRDSInfo(rds.Result);

            foreach (Task<List<ReactorEvent>> task in toolEvents)
            {
                ToolEventView toolEvent = new(task.Result,
                    report.StartDate.ToString(), currentDateTime.ToString(), task.Result[0].REACT_NO,
                    reactors.FirstOrDefault(x => x.ReactorNumber == int.Parse(task.Result[0].REACT_NO)).Type);

                toolEvent.SetDowntime(lastUpTransactions[toolEvent.Reactor].Result);

                report.ToolEvents.Add(toolEvent);
            }

            report.SetReactorInfo(reactors, GetUnscheduledReactors(report));

            report.ToolEvents = report.ToolEvents
                .OrderBy(x => x.Reactor)
                .ToList();

            foreach (Task<ToolEvent> task in cleanEvents)
                report.CleanEvents.Add(task.Result);

            foreach (Task<ToolEvent> task in metrologyEvents)
                report.MetrologyEvents.Add(task.Result);

            report.CleanEvents = report.CleanEvents.Where(x => (x.TOOL_MODE != "PROD" || x.TOOL_MODE_DESC != "Production") && x.TOOL_MODE != "OUT").ToList();
            report.MetrologyEvents = report.MetrologyEvents.Where(x => (x.TOOL_MODE != "PROD" || x.TOOL_MODE_DESC != "Production") && x.TOOL_MODE != "OUT").ToList();

            report.QuarterStartDate = task3.Result;

            report.CurrentWeek.SetYieldInformation(task1.Result, report.QuarterlyTargets, report.QuarterStartDate, task5.Result.Outs - task5.Result.CustomerScrap - task5.Result.ManufacturingScrap - task5.Result.ProductionScrap);
            report.PreviousWeek.SetYieldInformation(task2.Result, report.QuarterlyTargets, report.QuarterStartDate, task6.Result.Outs - task6.Result.CustomerScrap - task6.Result.ManufacturingScrap - task6.Result.ProductionScrap);

            report.CurrentWeek.QTDOutsAndScrap = task4.Result;
            report.PreviousWeek.QTDOutsAndScrap = task4.Result;

            report.CurrentHotWORunning = task7.Result;
        }
        catch (Exception ex)
        {
            logger.LogCritical(ex, "Failed to retrieve data back from Scrape DB endpoints.");
        }

        report.ReverseLists();

        ManualReportEntries entries = report.ManualReportEntries;
        List<SLLTool> sll = report.SLLTools;

        try
        {
            JsonFileHandler.SaveJSONFile(entries, filePaths["DailyReport"]);
            JsonFileHandler.SaveJSONFile(sll, filePaths["SLL"]);
        }
        catch (Exception ex)
        {
            logger.LogCritical(ex, "Failed to save data back to JSON files.");
        }

        return report;
    }

    public static List<int> GetUnscheduledReactors(DailyReport report)
    {
        List<int> reactors = new();

        foreach (ToolEventView tool in report.ToolEvents)
        {
            if (tool.MostRecentEvent.REACT_MODE.ToUpper() == "IDLE")
            {
                reactors.Add(int.Parse(tool.Reactor));
            }
        }

        return reactors;
    }
}