using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using FootwearTest.Models;
using FootwearTest.Services;
using Microsoft.Extensions.Logging;

namespace FootwearTest.ViewModels;

public partial class MethodAViewModel : ViewModelBase
{
    private readonly IDeviceClient _deviceClient;
    private readonly DeviceSettings _settings;
    private readonly TestFormulaService _formulaService;
    private readonly TestRunRepository _repository;
    private readonly ILogger<MethodAViewModel> _logger;

    public MethodAViewModel(
        IDeviceClient deviceClient,
        DeviceSettings settings,
        TestFormulaService formulaService,
        TestRunRepository repository,
        ILogger<MethodAViewModel> logger)
    {
        _deviceClient = deviceClient;
        _settings = settings;
        _formulaService = formulaService;
        _repository = repository;
        _logger = logger;
        MeasureSkinResistanceCommand = new AsyncRelayCommand(MeasureSkinResistanceAsync);
        RunWholeShoeCommand = new AsyncRelayCommand(RunWholeShoeAsync);
        StopCommand = new AsyncRelayCommand(StopAsync);
    }

    public ObservableCollection<MethodASampleRecord> Samples { get; } = [];
    public IAsyncRelayCommand MeasureSkinResistanceCommand { get; }
    public IAsyncRelayCommand RunWholeShoeCommand { get; }
    public IAsyncRelayCommand StopCommand { get; }

    private string _sampleDescription = "整鞋样品 1# / 2#";
    private string _statusText = "待机";
    private bool _isRunning;
    private double _skinMoistureResistance = 6.0;
    private double _averageMoistureResistance;
    private double _averageThermalResistance;
    private double _moistureCvPercent;
    private double _thermalCvPercent;
    private string _resultText = "尚未开始试验";

    public string SampleDescription
    {
        get => _sampleDescription;
        set => SetProperty(ref _sampleDescription, value);
    }

    public string StatusText
    {
        get => _statusText;
        set => SetProperty(ref _statusText, value);
    }

    public bool IsRunning
    {
        get => _isRunning;
        set => SetProperty(ref _isRunning, value);
    }

    public double SkinMoistureResistance
    {
        get => _skinMoistureResistance;
        set => SetProperty(ref _skinMoistureResistance, value);
    }

    public double AverageMoistureResistance
    {
        get => _averageMoistureResistance;
        set => SetProperty(ref _averageMoistureResistance, value);
    }

    public double AverageThermalResistance
    {
        get => _averageThermalResistance;
        set => SetProperty(ref _averageThermalResistance, value);
    }

    public double MoistureCvPercent
    {
        get => _moistureCvPercent;
        set => SetProperty(ref _moistureCvPercent, value);
    }

    public double ThermalCvPercent
    {
        get => _thermalCvPercent;
        set => SetProperty(ref _thermalCvPercent, value);
    }

    public string ResultText
    {
        get => _resultText;
        set => SetProperty(ref _resultText, value);
    }

    private async Task MeasureSkinResistanceAsync()
    {
        if (IsRunning)
        {
            return;
        }

        try
        {
            _logger.LogInformation("Method A skin moisture resistance started. SampleDescription={SampleDescription}", SampleDescription);
            await EnsureConnectedAsync();
            IsRunning = true;
            StatusText = "皮肤湿阻测定中";
            Samples.Clear();
            await _deviceClient.SetOutputsAsync(true, true, true);

            for (var i = 1; i <= 30; i++)
            {
                await Task.Delay(60);
                var snapshot = await _deviceClient.ReadSnapshotAsync();
                var value = _formulaService.CalculateSkinMoistureResistance(
                    _settings.FootAreaSquareMeters,
                    5620.0,
                    1.0,
                    2809.0,
                    snapshot.EnvironmentHumidityPercent / 100.0,
                    snapshot.WaterLossGramsPerHour);
                SkinMoistureResistance = Math.Round(value, 3);
                Samples.Add(_formulaService.CalculateMethodASample(i, snapshot, _settings.FootAreaSquareMeters, SkinMoistureResistance));
            }

            await _deviceClient.SetOutputsAsync(false, true, false);
            UpdateMethodAResult("皮肤湿阻");
            var id = await SaveRunAsync("方法 A - 皮肤湿阻", true);
            StatusText = "皮肤湿阻测定完成";
            _logger.LogInformation("Method A skin moisture resistance completed. RecordId={RecordId}, Samples={Samples}, Result={ResultText}", id, Samples.Count, ResultText);
        }
        catch (Exception ex)
        {
            StatusText = $"皮肤湿阻测定失败: {ex.Message}";
            _logger.LogError(ex, "Method A skin moisture resistance failed. SampleDescription={SampleDescription}", SampleDescription);
            await StopOutputsAfterFailureAsync("方法 A 皮肤湿阻失败后关闭输出");
        }
        finally
        {
            IsRunning = false;
        }
    }

    private async Task RunWholeShoeAsync()
    {
        if (IsRunning)
        {
            return;
        }

        try
        {
            _logger.LogInformation("Method A whole shoe test started. SampleDescription={SampleDescription}", SampleDescription);
            await EnsureConnectedAsync();
            IsRunning = true;
            StatusText = "整鞋热阻/湿阻测定中";
            Samples.Clear();
            await _deviceClient.SetOutputsAsync(true, true, true);

            for (var i = 1; i <= 30; i++)
            {
                await Task.Delay(80);
                var snapshot = await _deviceClient.ReadSnapshotAsync();
                Samples.Add(_formulaService.CalculateMethodASample(i, snapshot, _settings.FootAreaSquareMeters, SkinMoistureResistance));
                UpdateMethodAResult("整鞋热阻/湿阻");
            }

            await _deviceClient.SetOutputsAsync(false, true, false);
            var passed = _formulaService.PassesTenPercentDeviation(Samples.Select(sample => sample.ThermalResistance));
            UpdateMethodAResult("整鞋热阻/湿阻");
            var id = await SaveRunAsync("方法 A - 整鞋热阻/湿阻", passed);
            StatusText = passed ? "试验完成" : "结果偏差超过 ±10%，建议重测";
            _logger.LogInformation("Method A whole shoe test completed. RecordId={RecordId}, Passed={Passed}, Samples={Samples}, Result={ResultText}", id, passed, Samples.Count, ResultText);
        }
        catch (Exception ex)
        {
            StatusText = $"整鞋热阻/湿阻测定失败: {ex.Message}";
            _logger.LogError(ex, "Method A whole shoe test failed. SampleDescription={SampleDescription}", SampleDescription);
            await StopOutputsAfterFailureAsync("方法 A 整鞋试验失败后关闭输出");
        }
        finally
        {
            IsRunning = false;
        }
    }

    private async Task StopAsync()
    {
        try
        {
            _logger.LogWarning("Operator stopped Method A test");
            await _deviceClient.SetOutputsAsync(false, false, false);
            IsRunning = false;
            StatusText = "已停止";
        }
        catch (Exception ex)
        {
            StatusText = $"停止失败: {ex.Message}";
            _logger.LogError(ex, "Method A stop command failed");
        }
    }

    private async Task EnsureConnectedAsync()
    {
        if (!_deviceClient.IsConnected)
        {
            await _deviceClient.ConnectAsync();
        }
    }

    private void UpdateMethodAResult(string stage)
    {
        AverageMoistureResistance = Math.Round(_formulaService.Average(Samples.Select(sample => sample.MoistureResistance)), 3);
        AverageThermalResistance = Math.Round(_formulaService.Average(Samples.Select(sample => sample.ThermalResistance)), 4);
        MoistureCvPercent = Math.Round(_formulaService.CoefficientOfVariationPercent(Samples.Select(sample => sample.MoistureResistance)), 2);
        ThermalCvPercent = Math.Round(_formulaService.CoefficientOfVariationPercent(Samples.Select(sample => sample.ThermalResistance)), 2);
        ResultText = $"{stage}: 湿阻 {AverageMoistureResistance:F3} Pa·m²/W，热阻 {AverageThermalResistance:F4} m²·℃/W，CV {ThermalCvPercent:F2}%";
    }

    private async Task StopOutputsAfterFailureAsync(string reason)
    {
        try
        {
            await _deviceClient.SetOutputsAsync(false, false, false);
            _logger.LogWarning("Outputs forced off after Method A failure. Reason={Reason}", reason);
        }
        catch (Exception stopEx)
        {
            _logger.LogError(stopEx, "Failed to force outputs off after Method A failure. Reason={Reason}", reason);
        }
    }

    private async Task<long> SaveRunAsync(string method, bool isValid)
    {
        var result = new MethodAResult(
            method,
            SkinMoistureResistance,
            AverageMoistureResistance,
            AverageThermalResistance,
            MoistureCvPercent,
            ThermalCvPercent,
            isValid);
        var data = JsonSerializer.Serialize(new { Result = result, Samples }, new JsonSerializerOptions { WriteIndented = true });
        return await _repository.SaveAsync(new TestRunRecord(
            0,
            method,
            SampleDescription,
            $"假脚 {_settings.MethodATargetTemperatureC:F1} ℃；环境 {_settings.EnvironmentTemperatureC:F1} ℃ / {_settings.EnvironmentHumidityPercent:F0}%；风速 {_settings.MethodAAirSpeedMetersPerSecond:F2} m/s",
            ResultText,
            data,
            isValid,
            DateTime.Now));
    }
}
