using Microsoft.Data.SqlClient;
using ReportingServices.Shared.Models.PlanningReport;
using ReportingServices.Shared.Models.ProductionReport;
using System.Data;

namespace ReportingServices.Shared.Repositories;

public class ScrapeDatabaseRepository : IScrapeDatabaseRepository
{
    private SqlConnection _connection;
    private readonly string _connectionString;

    public ScrapeDatabaseRepository(string connectionString) => _connectionString = connectionString;

    public void OpenConnection()
    {
        _connection ??= new SqlConnection(_connectionString);

        if (_connection.State != ConnectionState.Open)
            _connection.Open();
    }

    public void CloseConnection()
    {
        if (_connection.State != ConnectionState.Closed)
            _connection.Close();
    }

    public int GetNumberOfPartChanges(string startDate, string endDate)
    {
        int result = 0;

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT COUNT(*) FROM " +
                       "(SELECT REACTOR, COUNT(PROD_SPEC_ID) - 1 AS PCHANGE FROM " +
                       "(SELECT REACTOR, PROD_SPEC_ID, COUNT(WO) AS PSN_COUNT FROM RDS WHERE DATE_OUT BETWEEN @startDate AND @endDate GROUP BY REACTOR, PROD_SPEC_ID) AS t " +
                       "GROUP BY REACTOR) AS l WHERE PCHANGE > 0";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@startDate", startDate);
        _ = cmd.Parameters.AddWithValue("@endDate", endDate);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            _ = reader.Read();

            result = int.Parse(reader[0].ToString());
        }

        cmd.Dispose();

        CloseConnection();

        return result;
    }

    public List<ScrapByDay> GetScrapByDay(List<ReactorOutsByRDS> outs)
    {
        List<ScrapByDay> scrap = new();
        string rdsNumbers = "";

        foreach (ReactorOutsByRDS rout in outs)
            rdsNumbers = rdsNumbers + "'" + rout.RDS_NO + "', ";

        rdsNumbers = rdsNumbers[..^2];

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT " +
                        "  DATE_OUT," +
                        "  SUM(CUST_TOT_REJ) AS TOT_REJ_CUST," +
                        "  SUM(LSL_TOT_REJ) AS TOT_REJ_MANU," +
                        "  SUM(TW_PROD) AS TW_PROD " +
                        "FROM RDS " +
                        "WHERE SEQ IN (" + rdsNumbers + ") " +
                        "GROUP BY DATE_OUT " +
                        "ORDER BY 1 DESC";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read() && reader[0].ToString() != "1/1/1900 12:00:00 AM")
            {
                int twProd = string.IsNullOrEmpty(reader[3].ToString()) ? 0 : int.Parse(reader[3].ToString());
                int custScrap = string.IsNullOrEmpty(reader[1].ToString()) ? 0 : int.Parse(reader[1].ToString());
                int manuScrap = string.IsNullOrEmpty(reader[2].ToString()) ? 0 : int.Parse(reader[2].ToString());
                int totScrap = custScrap + manuScrap;

                scrap.Add(new ScrapByDay
                {
                    StartDate = reader[0].ToString(),
                    TW_PROD = twProd,
                    TOT_REJ_CUST = custScrap,
                    TOT_REJ_MANU = manuScrap,
                    TOT_REJ_WFRS = totScrap
                });
            }
        }

        cmd.Dispose();

        CloseConnection();

        return scrap;
    }

    public List<ReactorPSNWORuns> GetReactorPSNWORuns(string startDate, string endDate)
    {
        List<ReactorPSNWORuns> weeklyPartChanges = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT REACTOR, PROD_SPEC_ID, COUNT(WO) FROM RDS " +
                        "WHERE DATE_OUT BETWEEN @startDate AND @endDate " +
                        "GROUP BY REACTOR, PROD_SPEC_ID " +
                        "ORDER BY 1";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@startDate", startDate);
        _ = cmd.Parameters.AddWithValue("@endDate", endDate);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                weeklyPartChanges.Add(new ReactorPSNWORuns
                {
                    REACTOR = reader[0].ToString(),
                    PSN = reader[1].ToString(),
                    WO_COUNT = int.Parse(reader[2].ToString())
                });
        }

        cmd.Dispose();

        CloseConnection();

        return weeklyPartChanges;
    }

    public QuarterlyTargets GetQuarterlyTargets()
    {
        Dictionary<string, float> targets = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT THRU_TARGET, THRU_QTY, THRU_PCNT FROM FISCAL_QTR_TARGETS " +
                       " WHERE THRU_GROUP = 'TOT' " +
                       "   AND FISCAL_YR = " +
                       "    (SELECT FISCAL_YR FROM FISCAL_QTR " +
                       "      WHERE START_DT < SYSDATETIME() " +
                       "        AND END_DT > SYSDATETIME()) " +
                       "   AND FISCAL_QTR = " +
                       "    (SELECT FISCAL_QTR FROM FISCAL_QTR " +
                       "      WHERE START_DT < SYSDATETIME() " +
                       "        AND END_DT > SYSDATETIME()) " +
                       "UNION " +
                       "SELECT 'PlanWorkingDays' As THRU_TARGET," +
                       "       PLAN_WORKING_DAYS AS THRU_QTY," +
                       "       NULL AS THRU_PCNT" +
                       "  FROM FISCAL_QTR " +
                       " WHERE SYSDATETIME() BETWEEN START_DT AND END_DT";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                if (reader[0].ToString().ToUpper() == "YIELD")
                    targets.Add(reader[0].ToString(), float.Parse(reader[2].ToString()));
                else if (!string.IsNullOrEmpty(reader[1].ToString()))
                    targets.Add(reader[0].ToString(), int.Parse(reader[1].ToString()));
            }
        }

        cmd.Dispose();

        CloseConnection();

        QuarterlyTargets quarterlyTargets = new()
        {
            Reactor_Outs = (int)targets["Reactor_Outs"],
            Yield_Outs = (int)targets["Yield_Outs"],
            IFX_Scrap = (int)targets["IFX_Scrap"],
            Yield = targets["Yield"],
            PlanWorkingDays = (int)targets["PlanWorkingDays"]
        };

        return quarterlyTargets;
    }

    public List<Reactor> GetReactors()
    {
        List<Reactor> reactors = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT " +
                       "   REACT_NO, REACT_TYPE, SUSC_POCKET_SIZE, CASE WHEN ACTIVE_LL_DISABLED <> '' THEN 'TRUE' ELSE 'FALSE' END AS \"LL_DISABLED\" " +
                       "  FROM REACTOR " +
                       " WHERE REACT_ASSIGNMENT IS NOT NULL " +
                       "   AND REACT_ASSIGNMENT <> 'Out of Service' " +
                       "   AND REACT_ASSIGNMENT <> '' " +
                       "   AND CURR_MODE_KEY = ''";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                reactors.Add(new Reactor
                {
                    ReactorNumber = int.Parse(reader[0].ToString()),
                    Type = reader[1].ToString(),
                    PocketSize = reader[2].ToString(),
                    HasDisabledLoadlock = bool.Parse(reader[3].ToString())
                });
        }

        cmd.Dispose();

        CloseConnection();

        return reactors;
    }

    public List<RDS> GetRDSForLastDay(string date)
    {
        List<RDS> rdsList = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT rds.REACTOR, rds.REACTOR_TYPE, rds.DATE_OUT, " +
                       "CASE WHEN lay.UL_TEMP IS NULL THEN '1000' ELSE lay.UL_TEMP END, psn.LAYER_TYPE FROM RDS " +
                       "INNER JOIN RDS_LAYER lay ON lay.RDS_NO = SEQ " +
                       "INNER JOIN PROD_SPEC psn ON rds.PROD_SPEC_ID = psn.SEQ " +
                       "WHERE DATE_OUT >= @date";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@date", date);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                rdsList.Add(new RDS
                {
                    Reactor = int.Parse(reader[0].ToString()),
                    ReactorType = reader[1].ToString(),
                    DateOut = DateTime.Parse(reader[2].ToString()),
                    UnloadTemp = int.Parse(reader[3].ToString()),
                    LayerType = reader[4].ToString()
                });
        }

        cmd.Dispose();

        CloseConnection();

        return rdsList;
    }

    public List<ReactorOutsByRDS> GetRDSRunBetweenDates(string startDate, string endDate)
    {
        List<ReactorOutsByRDS> outs = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT SEQ, WFRS_OUT, DATE_OUT " +
                       "  FROM RDS " +
                       " WHERE DATE_OUT >= @startDate " +
                       "   AND DATE_OUT < @endDate " +
                       "ORDER BY DATE_OUT ASC";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@startDate", startDate);
        _ = cmd.Parameters.AddWithValue("@endDate", endDate);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                outs.Add(new ReactorOutsByRDS
                {
                    RDS_NO = reader[0].ToString(),
                    Units = reader[1].ToString(),
                    EndProcessTime = reader[2].ToString()
                });
        }

        cmd.Dispose();

        CloseConnection();

        return outs;
    }

    public List<ReactorEvent> GetReactorEvents(string startDate, string endDate, string reactorNumber)
    {
        List<ReactorEvent> events = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT " +
                       "   revnt.REACT_NO, " +
                       "   EVENT_DTM, " +
                       "   COMMENT AS REASON, " +
                       "   REACT_MODE, " +
                       "   (SELECT TOP 1 NOTES " +
                       "      FROM REACTOR_LOG_NOTES rnote " +
                       "     WHERE rmode.START_RL_ID = rnote.SEQ " +
                       "    ORDER BY MV_NO DESC) AS COMMENT, " +
                       "   (SELECT TOP 1 CONCAT(PROB_CAT_ID, ' - ', PROB_CAT_DESC) " +
                       "      FROM REACT_MODE_SERV_INFO srv " +
                       "	 WHERE rmode.REACT_NO = srv.REACT_NO " +
                       "	   AND rmode.START_DTM = srv.START_DTM " +
                       "    ORDER BY MV_NO DESC) AS CATEGORY " +
                       "  FROM REACT_EVENT revnt, REACT_MODE rmode " +
                       " WHERE EVENT_DTM > @startDate " +
                       "   AND EVENT_DTM < @endDate " +
                       "   AND revnt.REACT_NO = @reactorNumber " +
                       "   AND EVENT_DTM = START_DTM " +
                       "   AND revnt.REACT_NO = rmode.REACT_NO " +
                       "   AND LOGGED_BY = START_USER " +
                       "   AND REACT_MODE = MODE " +
                       "UNION ALL " +
                       "SELECT * " +
                       "  FROM  " +
                       "     (SELECT TOP 1 " +
                       "             revnt.REACT_NO, " +
                       "             EVENT_DTM, COMMENT AS REASON, " +
                       "             REACT_MODE, " +
                       "             (SELECT TOP 1 NOTES " +
                       "                FROM REACTOR_LOG_NOTES rnote " +
                       "               WHERE rmode.START_RL_ID = rnote.SEQ " +
                       "              ORDER BY MV_NO DESC) AS COMMENT, " +
                       "		     (SELECT TOP 1 CONCAT(PROB_CAT_ID, ' - ', PROB_CAT_DESC) " +
                       "		      FROM REACT_MODE_SERV_INFO srv " +
                       "		     WHERE rmode.REACT_NO = srv.REACT_NO " +
                       "		       AND rmode.START_DTM = srv.START_DTM " +
                       "		    ORDER BY MV_NO DESC) AS CATEGORY " +
                       "        FROM REACT_EVENT revnt, REACT_MODE rmode " +
                       "       WHERE EVENT_DTM < @startDate " +
                       "         AND revnt.REACT_NO = @reactorNumber " +
                       "         AND EVENT_DTM = START_DTM " +
                       "         AND revnt.REACT_NO = rmode.REACT_NO " +
                       "         AND LOGGED_BY = START_USER " +
                       "         AND REACT_MODE = MODE ORDER BY EVENT_DTM DESC) AS tbl1 " +
                       "ORDER BY EVENT_DTM ASC";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@startDate", startDate);
        _ = cmd.Parameters.AddWithValue("@endDate", endDate);
        _ = cmd.Parameters.AddWithValue("@reactorNumber", reactorNumber);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                events.Add(new ReactorEvent
                {
                    REACT_NO = reader[0].ToString(),
                    EVENT_DTM = reader[1].ToString(),
                    REASON = reader[2].ToString(),
                    REACT_MODE = reader[3].ToString(),
                    COMMENT = reader[4].ToString(),
                    CAT_PROB_DESC = reader[5].ToString()
                });
        }

        cmd.Dispose();

        CloseConnection();

        return events;
    }

    public ToolEvent GetLatestToolEvent(string toolID)
    {
        ToolEvent evnt = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT TOP 1 " +
                       "    TOOL_ID, " +
                       "    START_DTM, " +
                       "    TOOL_MODE, " +
                       "    TOOL_MODE_DESC " +
                       "  FROM TOOL_LOG " +
                       " WHERE TOOL_ID = @toolID " +
                       "ORDER BY START_DTM DESC";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@toolID", toolID);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                evnt = new ToolEvent
                {
                    TOOL_ID = reader[0].ToString(),
                    START_DTM = reader[1].ToString(),
                    TOOL_MODE = reader[2].ToString(),
                    TOOL_MODE_DESC = reader[3].ToString()
                };
        }

        cmd.Dispose();

        CloseConnection();

        return evnt;
    }

    public int GetLastUpTransaction(string reactorNumber)
    {
        int lastTransaction = 0;

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT TOP 1 DATEDIFF(MINUTE,START_DTM,SYSDATETIME()) " +
                       "  FROM REACT_MODE " +
                       " WHERE REACT_NO = @reactorNumber " +
                       "   AND (MODE = 'UP' OR MODE = 'UP_WITH_INCREASED_SAMPLING') " +
                       "ORDER BY START_DTM DESC";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@reactorNumber", reactorNumber);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                lastTransaction = int.Parse(reader[0].ToString());
        }

        cmd.Dispose();

        CloseConnection();

        return lastTransaction;
    }

    public OutsAndScrapTotal GetOutsAndScrapTotals(string startDate, string endDate)
    {
        OutsAndScrapTotal totals = new()
        {
            Outs = 0,
            CustomerScrap = 0,
            ManufacturingScrap = 0,
            ProductionScrap = 0
        };

        if (startDate == endDate)
            return totals;

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT SUM(WFRS_OUT) AS OUTS, " +
                       "       SUM(CUST_TOT_REJ) AS CUST, " +
                       "       SUM(LSL_TOT_REJ) AS MANU, " +
                       "       SUM(TW_PROD) AS PROD " +
                       "  FROM RDS " +
                       " WHERE DATE_OUT >= @startDate " +
                       "   AND DATE_OUT < @endDate";

        cmd.CommandText = query;
        _ = cmd.Parameters.AddWithValue("@startDate", startDate);
        _ = cmd.Parameters.AddWithValue("@endDate", endDate);

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                totals = new()
                {
                    Outs = int.Parse(reader[0].ToString()),
                    CustomerScrap = int.Parse(reader[1].ToString()),
                    ManufacturingScrap = int.Parse(reader[2].ToString()),
                    ProductionScrap = int.Parse(reader[3].ToString()),
                };
        }

        cmd.Dispose();

        CloseConnection();

        return totals;
    }

    public DateTime GetQuarterStartDate()
    {
        DateTime date = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT START_DT " +
                       "  FROM FISCAL_QTR " +
                       " WHERE START_DT < SYSDATETIME() " +
                       "   AND END_DT > SYSDATETIME()";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                date = DateTime.Parse(reader[0].ToString());
        }

        cmd.Dispose();

        CloseConnection();

        return date;
    }

    public List<HoldLot> GetCurrentHoldLots()
    {
        List<HoldLot> lots = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT WO_NO, " +
                       "       HOLD_START_DTM, " +
                       "       HOLD_START_USER, " +
                       "       HOLD_START_REASON, " +
                       "       SEQ, " +
                       "       REACTOR " +
                       "  FROM WO_MAT_HOLD_HISTORY, RDS " +
                       " WHERE HOLD_STOP_DTM = '1/1/1900' " +
                       "   AND HOLD_START_DTM > '1/1/2022' " +
                       "   AND HOLD_ENTITY_ID = SEQ " +
                       "ORDER BY HOLD_START_DTM ASC";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                lots.Add(new HoldLot
                {
                    WO_NO = reader[0].ToString(),
                    HOLD_DATE = DateTime.Parse(reader[1].ToString()),
                    HOLD_USER = reader[2].ToString(),
                    HOLD_REASON = reader[3].ToString(),
                    RDS_NO = reader[4].ToString(),
                    REACTOR = reader[5].ToString(),

                });
        }

        cmd.Dispose();

        CloseConnection();

        return lots;
    }

    public List<string> GetCurrentHotWORunning()
    {
        List<string> lots = new();

        OpenConnection();

        SqlCommand cmd = _connection.CreateCommand();

        string query = "SELECT REACT_NO " +
                       "  FROM SCHED_DET_NG schd " +
                       "INNER JOIN WO_LOG wlog ON WO = WO_NO " +
                       " WHERE STOP_DTM > SYSDATETIME() " +
                       "   AND START_DTM < SYSDATETIME() " +
                       "   AND HOT_FLAG = 1 " +
                       "ORDER BY REACT_NO";

        cmd.CommandText = query;

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
                lots.Add("R" + reader[0].ToString());
        }

        cmd.Dispose();

        CloseConnection();

        return lots;
    }
}