using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace XLAB
{
    internal sealed class VerificationReportsService
    {
        private const string ReportSourceSql = @"
FROM dbo.EKZMK m
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
LEFT JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
LEFT JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
LEFT JOIN dbo.FRPD customers ON customers.IDFRPD = z.IDFRPDV
WHERE m.DTMKFK IS NOT NULL
  AND (@DateFrom IS NULL OR m.DTMKFK >= @DateFrom)
  AND (@DateToExclusive IS NULL OR m.DTMKFK < @DateToExclusive)
  AND (@CustomerId IS NULL OR z.IDFRPDV = @CustomerId)
  AND (@MeasurementAreaId IS NULL OR tips.IDSPOI = @MeasurementAreaId)";

        public VerificationReportData LoadReport(VerificationReportFilter filter)
        {
            var normalizedFilter = NormalizeFilter(filter);

            using (var connection = ReferenceDirectorySqlHelpers.CreateConnection())
            {
                connection.Open();

                return new VerificationReportData
                {
                    Summary = LoadSummary(connection, normalizedFilter),
                    CustomerRows = LoadCustomerRows(connection, normalizedFilter),
                    MeasurementAreaRows = LoadMeasurementAreaRows(connection, normalizedFilter)
                };
            }
        }

        public IReadOnlyList<DirectoryLookupItem> LoadCustomerItems()
        {
            return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
SELECT DISTINCT
    customers.IDFRPD AS Id,
    customers.NMFRPD AS Name
FROM dbo.EKZMK m
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
JOIN dbo.FRPD customers ON customers.IDFRPD = z.IDFRPDV
WHERE m.DTMKFK IS NOT NULL
  AND NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N'') IS NOT NULL
ORDER BY customers.NMFRPD, customers.IDFRPD;");
        }

        public IReadOnlyList<DirectoryLookupItem> LoadMeasurementAreaItems()
        {
            return ReferenceDirectorySqlHelpers.LoadLookupItems(@"
SELECT DISTINCT
    areas.IDSPOI AS Id,
    areas.NMOI AS Name
FROM dbo.EKZMK m
JOIN dbo.EKZ z ON z.IDEKZ = m.IDEKZ
LEFT JOIN dbo.TPRZ tprz ON tprz.IDTPRZ = z.IDTPRZ
LEFT JOIN dbo.TIPS tips ON tips.IDTIPS = tprz.IDTIPS
JOIN dbo.SPOI areas ON areas.IDSPOI = tips.IDSPOI
WHERE m.DTMKFK IS NOT NULL
  AND NULLIF(LTRIM(RTRIM(areas.NMOI)), N'') IS NOT NULL
ORDER BY areas.NMOI, areas.IDSPOI;");
        }

        private static void AddFilterParameters(SqlCommand command, VerificationReportFilter filter)
        {
            command.CommandTimeout = ReferenceDirectorySqlHelpers.GetCommandTimeoutSeconds();
            ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@CustomerId", filter.CustomerId);
            ReferenceDirectorySqlHelpers.AddNullableIntParameter(command, "@MeasurementAreaId", filter.MeasurementAreaId);
            ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@DateFrom", filter.DateFrom);
            ReferenceDirectorySqlHelpers.AddNullableDateTimeParameter(command, "@DateToExclusive", GetDateToExclusive(filter.DateTo));
        }

        private static DateTime? GetDateToExclusive(DateTime? dateTo)
        {
            return dateTo.HasValue ? dateTo.Value.Date.AddDays(1) : (DateTime?)null;
        }

        private static VerificationReportSummary LoadSummary(SqlConnection connection, VerificationReportFilter filter)
        {
            var sql = @"
SELECT
    COUNT(*) AS TotalCount,
    COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
    COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
    COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
    COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
" + ReportSourceSql + ";";

            using (var command = new SqlCommand(sql, connection))
            {
                AddFilterParameters(command, filter);

                using (var reader = command.ExecuteReader())
                {
                    if (!reader.Read())
                    {
                        return new VerificationReportSummary();
                    }

                    return new VerificationReportSummary
                    {
                        TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
                        GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
                        RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
                        WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
                        IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
                    };
                }
            }
        }

        private static IReadOnlyList<VerificationReportCustomerRow> LoadCustomerRows(SqlConnection connection, VerificationReportFilter filter)
        {
            var sql = @"
SELECT
    z.IDFRPDV AS CustomerId,
    COALESCE(NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N''), N'Не указано') AS CustomerName,
    COUNT(*) AS TotalCount,
    COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
    COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
    COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
    COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
" + ReportSourceSql + @"
GROUP BY
    z.IDFRPDV,
    COALESCE(NULLIF(LTRIM(RTRIM(customers.NMFRPD)), N''), N'Не указано')
ORDER BY
    CustomerName;";

            var rows = new List<VerificationReportCustomerRow>();

            using (var command = new SqlCommand(sql, connection))
            {
                AddFilterParameters(command, filter);

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        rows.Add(new VerificationReportCustomerRow
                        {
                            CustomerId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "CustomerId"),
                            CustomerName = ReferenceDirectorySqlHelpers.GetString(reader, "CustomerName"),
                            TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
                            GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
                            RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
                            WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
                            IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
                        });
                    }
                }
            }

            return rows;
        }

        private static IReadOnlyList<VerificationReportMeasurementAreaRow> LoadMeasurementAreaRows(SqlConnection connection, VerificationReportFilter filter)
        {
            var sql = @"
SELECT
    tips.IDSPOI AS MeasurementAreaId,
    COALESCE(NULLIF(LTRIM(RTRIM(areas.NMOI)), N''), N'Не указано') AS MeasurementAreaName,
    COUNT(*) AS TotalCount,
    COALESCE(SUM(CASE WHEN m.GDN = 1 THEN 1 ELSE 0 END), 0) AS GoodCount,
    COALESCE(SUM(CASE WHEN m.GDN = 0 THEN 1 ELSE 0 END), 0) AS RejectedCount,
    COALESCE(SUM(CASE WHEN m.GDN IS NULL THEN 1 ELSE 0 END), 0) AS WithoutResultCount,
    COALESCE(SUM(CASE WHEN m.DTVDM IS NOT NULL THEN 1 ELSE 0 END), 0) AS IssuedCount
" + ReportSourceSql + @"
GROUP BY
    tips.IDSPOI,
    COALESCE(NULLIF(LTRIM(RTRIM(areas.NMOI)), N''), N'Не указано')
ORDER BY
    MeasurementAreaName;";

            var rows = new List<VerificationReportMeasurementAreaRow>();

            using (var command = new SqlCommand(sql, connection))
            {
                AddFilterParameters(command, filter);

                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        rows.Add(new VerificationReportMeasurementAreaRow
                        {
                            MeasurementAreaId = ReferenceDirectorySqlHelpers.GetNullableInt32(reader, "MeasurementAreaId"),
                            MeasurementAreaName = ReferenceDirectorySqlHelpers.GetString(reader, "MeasurementAreaName"),
                            TotalCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "TotalCount"),
                            GoodCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "GoodCount"),
                            RejectedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "RejectedCount"),
                            WithoutResultCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "WithoutResultCount"),
                            IssuedCount = ReferenceDirectorySqlHelpers.GetInt32(reader, "IssuedCount")
                        });
                    }
                }
            }

            return rows;
        }

        private static VerificationReportFilter NormalizeFilter(VerificationReportFilter filter)
        {
            var normalizedFilter = filter ?? new VerificationReportFilter();
            var dateFrom = normalizedFilter.DateFrom.HasValue ? normalizedFilter.DateFrom.Value.Date : (DateTime?)null;
            var dateTo = normalizedFilter.DateTo.HasValue ? normalizedFilter.DateTo.Value.Date : (DateTime?)null;

            if (dateFrom.HasValue && dateTo.HasValue && dateFrom.Value > dateTo.Value)
            {
                throw new InvalidOperationException("Дата \"с\" не может быть позже даты \"по\".");
            }

            return new VerificationReportFilter
            {
                CustomerId = normalizedFilter.CustomerId,
                MeasurementAreaId = normalizedFilter.MeasurementAreaId,
                DateFrom = dateFrom,
                DateTo = dateTo
            };
        }
    }
}
