using System;
using System.Collections.Generic;
using System.Linq;
using FootwearTest.Models;

namespace FootwearTest.Services;

public sealed class TestFormulaService
{
    private const double VaporizationHeatWhPerGram = 0.672;

    public double CalculateSkinMoistureResistance(
        double footAreaSquareMeters,
        double skinSaturatedVaporPressurePa,
        double skinRelativeHumidity,
        double environmentSaturatedVaporPressurePa,
        double environmentRelativeHumidity,
        double nakedFootSweatGramsPerHour)
    {
        return footAreaSquareMeters *
            (skinSaturatedVaporPressurePa * skinRelativeHumidity - environmentSaturatedVaporPressurePa * environmentRelativeHumidity) /
            (VaporizationHeatWhPerGram * Math.Max(nakedFootSweatGramsPerHour, 0.001));
    }

    public MethodASampleRecord CalculateMethodASample(
        int index,
        DeviceSnapshot snapshot,
        double footAreaSquareMeters,
        double skinMoistureResistance,
        double skinSaturatedVaporPressurePa = 5623.0,
        double environmentSaturatedVaporPressurePa = 2809.0)
    {
        var he = VaporizationHeatWhPerGram * Math.Max(snapshot.WaterLossGramsPerHour, 0.001);
        var re = footAreaSquareMeters *
            (skinSaturatedVaporPressurePa - environmentSaturatedVaporPressurePa * snapshot.EnvironmentHumidityPercent / 100.0) /
            he - skinMoistureResistance;
        var hd = Math.Max(snapshot.PowerWatts - he, 0.001);
        var rt = footAreaSquareMeters * (snapshot.FootTemperatureC - snapshot.EnvironmentTemperatureC) / hd;

        return new MethodASampleRecord(
            index,
            snapshot.Timestamp,
            snapshot.FootTemperatureC,
            snapshot.EnvironmentTemperatureC,
            snapshot.EnvironmentHumidityPercent,
            snapshot.WaterLossGramsPerHour,
            snapshot.PowerWatts,
            Math.Round(he, 3),
            Math.Round(hd, 3),
            Math.Round(re, 3),
            Math.Round(rt, 4));
    }

    public double Average(IEnumerable<double> values)
    {
        var list = values.ToArray();
        return list.Length == 0 ? 0 : list.Average();
    }

    public double CoefficientOfVariationPercent(IEnumerable<double> values)
    {
        var list = values.ToArray();
        if (list.Length < 2)
        {
            return 0;
        }

        var average = list.Average();
        if (Math.Abs(average) < 0.000001)
        {
            return 0;
        }

        var variance = list.Sum(value => Math.Pow(value - average, 2)) / (list.Length - 1);
        return Math.Sqrt(variance) / Math.Abs(average) * 100.0;
    }

    public bool PassesTenPercentDeviation(IEnumerable<double> values)
    {
        var list = values.ToArray();
        if (list.Length == 0)
        {
            return false;
        }

        var average = list.Average();
        return list.All(value => Math.Abs(value - average) <= Math.Abs(average) * 0.10);
    }

    public MethodBMoistureResult CalculateMethodBMoisture(
        double m11,
        double m12,
        double m21,
        double m22,
        double m31,
        double m32,
        double m41,
        double m42,
        double m51,
        double m52,
        double m61,
        double m62,
        double m71,
        double m72)
    {
        var m5 = m51 - m52;
        var m6 = m61 - m62;
        var m7 = m71 - m72;
        var m8 = (m6 + m7) / 2.0;
        var m180 = m5 - m8;
        var scale = 15.0 / Math.Max(m180, 0.001);

        var m1 = m12 - m11;
        var m2 = m22 - m21;
        var m3 = m32 - m31;
        var m4 = m42 - m41;
        var t180 = m180 - m1 - m2 - m3 - m4;

        return new MethodBMoistureResult(
            Math.Round(m1, 3),
            Math.Round(m2, 3),
            Math.Round(m3, 3),
            Math.Round(m4, 3),
            Math.Round(m5, 3),
            Math.Round(m6, 3),
            Math.Round(m7, 3),
            Math.Round(m8, 3),
            Math.Round(m180, 3),
            Math.Round(t180, 3),
            Math.Round(m1 * scale, 3),
            Math.Round(m2 * scale, 3),
            Math.Round(m3 * scale, 3),
            Math.Round(m4 * scale, 3),
            Math.Round(t180 * scale, 3),
            Math.Round((t180 + m3 + m4) * scale, 3),
            m180 >= 14.1 && m180 <= 15.9);
    }

    public MethodBWarmthResult CalculateMethodBWarmth(
        double energyKilojoules,
        double seconds,
        double footAreaSquareMeters,
        double footTemperatureC,
        double chamberTemperatureC)
    {
        var heatWatts = energyKilojoules / Math.Max(seconds, 1.0) * 1000.0;
        var resistance = footAreaSquareMeters * (footTemperatureC - chamberTemperatureC) / Math.Max(heatWatts, 0.001);
        return new MethodBWarmthResult(
            Math.Round(energyKilojoules, 1),
            seconds,
            Math.Round(heatWatts, 3),
            Math.Round(footTemperatureC, 2),
            Math.Round(chamberTemperatureC, 2),
            Math.Round(resistance, 3));
    }
}
