using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Models.Stateless;
using OI.Metrology.Shared.Repositories;
using System.Data;
using System.Data.Common;
using System.Text;
using System.Text.Json;

namespace OI.Metrology.Server.Repository;

public class InfinityQSRepository : IInfinityQSRepository
{

    private readonly string _MockRoot;
    private readonly string _RepositoryName;
    private readonly IDbConnectionFactory _DBConnectionFactory;

    public InfinityQSRepository(string mockRoot, IDbConnectionFactory dbConnectionFactory)
    {
        _MockRoot = mockRoot;
        _DBConnectionFactory = dbConnectionFactory;
        _RepositoryName = nameof(InfinityQSRepository)[..^10];
    }

    string IInfinityQSRepository.GetCommandText(string subGroupId)
    {
        StringBuilder result = new();
        if (string.IsNullOrEmpty(subGroupId))
            throw new ArgumentException(nameof(subGroupId));
        _ = result
            .AppendLine(" select ")
            .AppendLine("       sd.f_sgrp sd_sgrp, ")
            .AppendLine("       sd.f_tsno sd_tsno, ")
            .AppendLine("       dd.f_dsgp dd_dsgp, ")
            .AppendLine("       dg.f_name gd_name, ")
            .AppendLine("       dd.f_name dd_name ")
            .AppendLine(" from [SPCEPIWORLD].[dbo].[SGRP_DSC] sd ")
            .AppendLine(" join [SPCEPIWORLD].[dbo].[DESC_DAT] dd ")
            .AppendLine("   on sd.f_dsgp = dd.f_dsgp ")
            .AppendLine("  and sd.f_desc = dd.f_desc ")
            .AppendLine(" join [SPCEPIWORLD].[dbo].[DESC_GRP] dg ")
            .AppendLine("   on dd.f_dsgp = dg.f_dsgp ");
        _ = result.Append(" where sd.f_sgrp = ").Append(subGroupId).AppendLine(" ");
        _ = result.AppendLine(" for json path ");
        return result.ToString();
    }

    string IInfinityQSRepository.GetCommandText(string? subGroupId, string? process, string? job, string? part, string? lot, string? dateTime)
    {
        StringBuilder result = new();
        const string dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
        if (!string.IsNullOrEmpty(dateTime) && (dateTime.Contains('-') || dateTime.Contains(' ') || dateTime.Contains(':')) && dateTime.Length != dateTimeFormat.Length)
            throw new ArgumentException(nameof(dateTime));
        _ = result
                .AppendLine(" select iq.ev_count, iq.cl_count, iq.sl_count, iq.se_sgrp, iq.se_sgtm, iq.se_tsno, iq.td_test, iq.pr_name, iq.jd_name, iq.pl_name, iq.pd_name, iq.td_name, iq.se_val ")
                .AppendLine(" from ( ")
                .AppendLine(" select ")
                .AppendLine("       se.f_sgrp se_sgrp, ")
                .AppendLine("       se.f_sgtm se_sgtm, ")
                .AppendLine("       se.f_tsno se_tsno, ")
                .AppendLine("       se.f_val se_val, ")
                .AppendLine("       pr.f_name pr_name, ")
                .AppendLine("       jd.f_name jd_name, ")
                .AppendLine("       pl.f_name pl_name, ")
                .AppendLine("       pd.f_name pd_name, ")
                .AppendLine("       td.f_test td_test, ")
                .AppendLine("       td.f_name td_name, ")
                .AppendLine("       (select count(cl.f_part) ")
                .AppendLine("       from [spcepiworld].[dbo].[ctrl_lim] cl ")
                .AppendLine("       where cl.f_part = pd.f_part ")
                .AppendLine("         and cl.f_test = td.f_test ")
                .AppendLine("       ) cl_count, ")
                .AppendLine("       (select count(sl.f_part) ")
                .AppendLine("       from [spcepiworld].[dbo].[spec_lim] sl ")
                .AppendLine("       where sl.f_part = pd.f_part ")
                .AppendLine("         and sl.f_test = td.f_test ")
                .AppendLine("       ) sl_count, ")
                .AppendLine("       (select count(ev.f_evnt) ")
                .AppendLine("       from [spcepiworld].[dbo].[evnt_inf] ev ")
                .AppendLine("       where ev.f_prcs = pr.f_prcs ")
                .AppendLine("         and ev.f_part = pd.f_part ")
                .AppendLine("         and ev.f_sgtm = se.f_sgtm ")
                .AppendLine("       ) ev_count ")
                .AppendLine(" from [spcepiworld].[dbo].[sgrp_ext] se ")
                .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] pr ")
                .AppendLine("   on se.f_prcs = pr.f_prcs ")
                .AppendLine(" join [spcepiworld].[dbo].[job_dat] jd ")
                .AppendLine("   on se.f_job = jd.f_job ")
                .AppendLine(" join [spcepiworld].[dbo].[part_lot] pl ")
                .AppendLine("   on se.f_lot = pl.f_lot ")
                .AppendLine(" join [spcepiworld].[dbo].[part_dat] pd ")
                .AppendLine("   on se.f_part = pd.f_part ")
                .AppendLine(" join [spcepiworld].[dbo].[test_dat] td  ")
                .AppendLine("   on se.f_test = td.f_test ")
                .AppendLine(" where se.f_flag = 0 ");
        if (!string.IsNullOrEmpty(subGroupId))
            _ = result.Append("   and se.f_sgrp = ").Append(subGroupId.Split(" ")[0]).AppendLine("  ");
        if (!string.IsNullOrEmpty(process))
            _ = result.Append("   and pr.f_name = '").Append(process).AppendLine("' ");
        if (!string.IsNullOrEmpty(part))
            _ = result.Append("   and pd.f_name = '").Append(part).AppendLine("' ");
        if (!string.IsNullOrEmpty(job))
            _ = result.Append("   and jd.f_name = '").Append(job).AppendLine("' ");
        if (!string.IsNullOrEmpty(lot))
            _ = result.Append("   and pl.f_name = '").Append(lot).AppendLine("' ");
        if (!string.IsNullOrEmpty(dateTime) && (dateTime.Contains('-') || dateTime.Contains(' ') || dateTime.Contains(':')))
            _ = result.Append("   and dateadd(HH, -7, (dateadd(SS, convert(bigint, se.f_sgtm), '19700101'))) = '").Append(dateTime).AppendLine("' ");
        _ = result.AppendLine(" ) as iq ");
        _ = result.AppendLine(" order by iq.ev_count desc, iq.cl_count desc, iq.sl_count desc, iq.se_sgrp, iq.se_tsno, iq.td_test ");
        _ = result.AppendLine("   for json path ");
        return result.ToString();
    }

    private static StringBuilder GetForJsonPath(IDbConnectionFactory dbConnectionFactory, string commandText)
    {
        StringBuilder stringBuilder = new();
        using DbConnection dbConnection = dbConnectionFactory.GetDbConnection();
        DbCommand dbCommand = dbConnection.CreateCommand();
        dbCommand.CommandText = commandText;
        DbDataReader dbDataReader = dbCommand.ExecuteReader(CommandBehavior.SequentialAccess);
        while (dbDataReader.Read())
            _ = stringBuilder.Append(dbDataReader.GetString(0));
        return stringBuilder;
    }

    private static InfinityQSBase GetInfinityQSBase(IDbConnectionFactory dbConnectionFactory, IInfinityQSRepository infinityQSRepository, string subGroupId)
    {
        InfinityQSBase result;
        string commandText = infinityQSRepository.GetCommandText(subGroupId, process: string.Empty, job: string.Empty, part: string.Empty, lot: string.Empty, dateTime: string.Empty);
        StringBuilder stringBuilder = GetForJsonPath(dbConnectionFactory, commandText);
        InfinityQSBase[]? results = JsonSerializer.Deserialize<InfinityQSBase[]>(stringBuilder.ToString(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
        if (results is null)
            throw new NullReferenceException(nameof(results));
        // if (results.Select(l => l.SE_SGRP).Distinct().Count() != 1)
        //     throw new NotSupportedException("Multiple ids are present!");
        result = (from l in results orderby l.SE_SGRP, l.SE_TSNO, l.TD_TEST select l).First();
        return result;
    }

    Result<InfinityQSBase[]> IInfinityQSRepository.GetData(string subGroupId)
    {
        Result<InfinityQSBase[]>? result;
        if (!string.IsNullOrEmpty(_MockRoot))
        {
            string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSRepository.GetData)}.json"));
            result = JsonSerializer.Deserialize<Result<InfinityQSBase[]>>(json);
            if (result is null)
                throw new NullReferenceException(nameof(result));
        }
        else
        {
            IInfinityQSRepository infinityQSRepository = this;
            InfinityQSBase infinityQSBase = GetInfinityQSBase(_DBConnectionFactory, infinityQSRepository, subGroupId);
            string commandText = infinityQSRepository.GetCommandText(subGroupId, process: infinityQSBase.PR_NAME, job: infinityQSBase.JD_NAME, part: infinityQSBase.PD_NAME, lot: infinityQSBase.PL_NAME, dateTime: string.Concat(infinityQSBase.SE_SGTM));
            StringBuilder stringBuilder = GetForJsonPath(_DBConnectionFactory, commandText);
            InfinityQSBase[]? results = JsonSerializer.Deserialize<InfinityQSBase[]>(stringBuilder.ToString(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            if (results is null)
                throw new NullReferenceException(nameof(results));
            result = new()
            {
                Results = results,
                TotalRows = results.Length,
            };
        }
        return result;
    }

    Result<InfinityQSDescriptor[]> IInfinityQSRepository.GetDescriptors(string subGroupId)
    {
        Result<InfinityQSDescriptor[]>? result;
        if (!string.IsNullOrEmpty(_MockRoot))
        {
            string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSRepository.GetDescriptors)}.json"));
            result = JsonSerializer.Deserialize<Result<InfinityQSDescriptor[]>>(json);
            if (result is null)
                throw new NullReferenceException(nameof(result));
        }
        else
        {
            IInfinityQSRepository infinityQSRepository = this;
            string commandText = infinityQSRepository.GetCommandText(subGroupId);
            StringBuilder stringBuilder = GetForJsonPath(_DBConnectionFactory, commandText);
            InfinityQSDescriptor[]? results = JsonSerializer.Deserialize<InfinityQSDescriptor[]>(stringBuilder.ToString(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            if (results is null)
                throw new NullReferenceException(nameof(results));
            result = new()
            {
                Results = results,
                TotalRows = results.Length,
            };
        }
        return result;
    }

    string IInfinityQSRepository.GetCommandText(InfinityQSBase infinityQSBase)
    {
        StringBuilder result = new();
        if (string.IsNullOrEmpty(infinityQSBase.PR_NAME))
            throw new ArgumentException(nameof(infinityQSBase.PR_NAME));
        if (string.IsNullOrEmpty(infinityQSBase.PD_NAME))
            throw new ArgumentException(nameof(infinityQSBase.PD_NAME));
        _ = result
            .AppendLine(" select ")
            .AppendLine(" ev.f_evnt [ev_evnt], ")
            .AppendLine(" ev.f_sgtm [ev_sgtm], ")
            .AppendLine(" dateadd(HH, -7, (dateadd(SS, convert(bigint, ev.f_sgtm), '19700101'))) [ev_utc7], ")
            .AppendLine(" pr.f_name [pr_name], ")
            .AppendLine(" pd.f_name [pd_name], ")
            .AppendLine(" td.f_test [td_test], ")
            .AppendLine(" td.f_name [td_name], ")
            .AppendLine(" ev.f_name [ev_name] ")
            .AppendLine(" from [spcepiworld].[dbo].[evnt_inf] ev ")
            .AppendLine(" join [spcepiworld].[dbo].[prcs_dat] pr ")
            .AppendLine(" on ev.f_prcs = pr.f_prcs ")
            .AppendLine(" join [spcepiworld].[dbo].[part_dat] pd ")
            .AppendLine(" on ev.f_part = pd.f_part ")
            .AppendLine(" join [spcepiworld].[dbo].[test_dat] td ")
            .AppendLine(" on ev.f_test = td.f_test ")
            .Append(" where pr.f_name = '").Append(infinityQSBase.PR_NAME).AppendLine("' ")
            .Append(" and pd.f_name = '").Append(infinityQSBase.PD_NAME).AppendLine("' ")
            .Append(" and ev.f_sgtm = ").Append(infinityQSBase.SE_SGTM).AppendLine(" ")
            .AppendLine(" for json path ");
        return result.ToString();
    }

    Result<InfinityQSEvent[]> IInfinityQSRepository.GetEvents(string subGroupId)
    {
        Result<InfinityQSEvent[]>? result;
        if (!string.IsNullOrEmpty(_MockRoot))
        {
            string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSRepository.GetEvents)}.json"));
            result = JsonSerializer.Deserialize<Result<InfinityQSEvent[]>>(json);
            if (result is null)
                throw new NullReferenceException(nameof(result));
        }
        else
        {
            InfinityQSEvent[]? results;
            IInfinityQSRepository infinityQSRepository = this;
            InfinityQSBase infinityQSBase = GetInfinityQSBase(_DBConnectionFactory, infinityQSRepository, subGroupId);
            if (infinityQSBase.EV_COUNT <= 0)
                results = Array.Empty<InfinityQSEvent>();
            else
            {
                string commandText = infinityQSRepository.GetCommandText(infinityQSBase);
                StringBuilder stringBuilder = GetForJsonPath(_DBConnectionFactory, commandText);
                results = JsonSerializer.Deserialize<InfinityQSEvent[]>(stringBuilder.ToString(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
                if (results is null)
                    throw new NullReferenceException(nameof(results));
            }
            result = new()
            {
                Results = results,
                TotalRows = results.Length,
            };
        }
        return result;
    }

    Result<InfinityQSBase[]> IInfinityQSRepository.GetHeader(string subGroupId)
    {
        Result<InfinityQSBase[]>? result;
        if (!string.IsNullOrEmpty(_MockRoot))
        {
            string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IInfinityQSRepository.GetHeader)}.json"));
            result = JsonSerializer.Deserialize<Result<InfinityQSBase[]>>(json);
            if (result is null)
                throw new NullReferenceException(nameof(result));
        }
        else
        {
            IInfinityQSRepository infinityQSRepository = this;
            InfinityQSBase infinityQSBase = GetInfinityQSBase(_DBConnectionFactory, infinityQSRepository, subGroupId);
            InfinityQSBase[] results = new InfinityQSBase[] { infinityQSBase };
            result = new()
            {
                Results = results,
                TotalRows = results.Length,
            };
        }
        return result;
    }

}