using MainShell.Common.ViewModel;
using MainShell.Common;
using MainShell.Filewritable;
using MainShell.Hardware;
using MainShell.Models;
using MainShell.Recipe.Models;
using MainShell.Recipe.Models.SubstrateParameter;
using MainShell.Recipe.View;
using MaxwellFramework.Core.Common.Command;
using Microsoft.Win32;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace MainShell.Recipe.ViewModel
{
    public class MarkTeachViewModel : PropertyChangedBase
    {
        private const double PitchTolerance = 0.001d;

        private readonly HardwareManager _hardwareManager;
        private readonly IWindowManager _windowManager;
        private SubstrateInfo _observedSubstrateInfo;
        private MarkCoordinatePreviewWindow _coordinatePreviewWindow;
        private MarkCoordinatePreviewWindowModel _coordinatePreviewWindowModel;

        public MarkTeachViewModel(HardwareManager hardwareManager,IWindowManager windowManager)
        {
            _hardwareManager = hardwareManager;
            _windowManager = windowManager;
            AddMarkCmd = new DelegateCommand(AddMark);
            DeleteMarkCmd = new DelegateCommand(DeleteMark);
            TeachMarkCmd = new DelegateCommand(TeachMark);
            MoveToMarkCmd = new DelegateCommand(MoveToMark);
            MarkModelCmd = new DelegateCommand(MarkModel);
            VisionParSetCmd = new DelegateCommand(VisionParSet);
            GenerateCoordinatesCmd = new DelegateCommand(GenerateCoordinates);
            ImportCoordinatesCmd = new DelegateCommand(ImportCoordinates);
            ClearCoordinatesCmd = new DelegateCommand(ClearCoordinates);
            ShowCoordinatePreviewCmd = new DelegateCommand(ShowCoordinatePreview);
            RefreshTemplates();
        }

        public ObservableCollection<string> AvailableTemplates { get; set; } = new ObservableCollection<string>();

        private void RefreshTemplates()
        {
            AvailableTemplates.Clear();
            try 
            {
                if (Directory.Exists(Paths.TemplateBasePath))
                {
                    var files = Directory.GetFiles(Paths.TemplateBasePath);
                    foreach (var file in files)
                    {
                        AvailableTemplates.Add(Path.GetFileName(file));
                    }
                }
            }
            catch (Exception)
            {
                // Handle or log exception
            }
        }

        private SubstrateRecipe _substrateRecipe;
        public SubstrateRecipe SubstrateRecipe
        {
            get => _substrateRecipe;
            set
            {
                if (SetAndNotify(ref _substrateRecipe, value))
                {
                    EnsureCoordinateGenerationState();
                    ObserveSubstrateInfo(value?.SubstrateInfo);
                    NotifyOfPropertyChange(() => MarkPars);
                    NotifyOfPropertyChange(() => MarkDatas);
                    NotifyOfPropertyChange(() => CoordinateGenerationState);
                    NotifyOfPropertyChange(() => CoordinatePreviewPoints);
                }
            }
        }

        public SubtrateMarkPars MarkPars => SubstrateRecipe?.SubtrateMarkParameterInfo;
        public ObservableCollection<MarkData> MarkDatas => MarkPars?.MarkDatas;
        public MarkCoordinateGenerationState CoordinateGenerationState => MarkPars?.CoordinateGenerationState;
        public ObservableCollection<MarkCoordinatePoint> CoordinatePreviewPoints => CoordinateGenerationState?.Points;

        private MarkData _selectedMarkData;
        public MarkData SelectedMarkData
        {
            get => _selectedMarkData;
            set => SetAndNotify(ref _selectedMarkData, value);
        }
        private bool _isTeachPose;

        public bool IsTeachPose
        {
            get { return _isTeachPose; }
            set { SetAndNotify(ref _isTeachPose, value); } 
        }
	
        #region Commands

        public ICommand AddMarkCmd { get; }
        public ICommand DeleteMarkCmd { get; }
        public ICommand TeachMarkCmd { get; }
        public ICommand MoveToMarkCmd { get; }
        public ICommand MarkModelCmd { get; }
        public ICommand VisionParSetCmd { get; }
        public ICommand GenerateCoordinatesCmd { get; }
        public ICommand ImportCoordinatesCmd { get; }
        public ICommand ClearCoordinatesCmd { get; }
        public ICommand ShowCoordinatePreviewCmd { get; }

        private void ObserveSubstrateInfo(SubstrateInfo substrateInfo)
        {
            if (_observedSubstrateInfo != null)
            {
                _observedSubstrateInfo.PropertyChanged -= OnSubstrateInfoChanged;
            }

            _observedSubstrateInfo = substrateInfo;

            if (_observedSubstrateInfo != null)
            {
                _observedSubstrateInfo.PropertyChanged += OnSubstrateInfoChanged;
            }

            SyncCoordinateGenerationSource();
        }

        private void OnSubstrateInfoChanged(object sender, PropertyChangedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.PropertyName)
                || e.PropertyName == nameof(SubstrateInfo.RowNumber)
                || e.PropertyName == nameof(SubstrateInfo.ColNumber)
                || e.PropertyName == nameof(SubstrateInfo.PitchX)
                || e.PropertyName == nameof(SubstrateInfo.PitchY))
            {
                SyncCoordinateGenerationSource();
            }
        }

        private bool EnsureCoordinateGenerationState()
        {
            if (SubstrateRecipe == null)
            {
                return false;
            }

            if (SubstrateRecipe.SubtrateMarkParameterInfo == null)
            {
                SubstrateRecipe.SubtrateMarkParameterInfo = new SubtrateMarkPars();
            }

            if (SubstrateRecipe.SubtrateMarkParameterInfo.CoordinateGenerationState == null)
            {
                SubstrateRecipe.SubtrateMarkParameterInfo.CoordinateGenerationState = new MarkCoordinateGenerationState();
            }

            return true;
        }

        private void SyncCoordinateGenerationSource()
        {
            if (!EnsureCoordinateGenerationState())
            {
                return;
            }

            var substrateInfo = SubstrateRecipe?.SubstrateInfo;
            var coordinateState = CoordinateGenerationState;
            if (substrateInfo == null || coordinateState == null)
            {
                return;
            }

            coordinateState.Rows = substrateInfo.RowNumber;
            coordinateState.Cols = substrateInfo.ColNumber;
            coordinateState.PitchX = substrateInfo.PitchX;
            coordinateState.PitchY = substrateInfo.PitchY;

            NotifyOfPropertyChange(() => CoordinateGenerationState);
            NotifyOfPropertyChange(() => CoordinatePreviewPoints);
        }

        private bool EnsureRecipeSelected()
        {
            if (SubstrateRecipe != null)
            {
                return true;
            }

            MwMessageBox.Show("请先选择或创建基板配方", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
            return false;
        }

        private void AddMark(object obj)
        {
            if (MarkDatas == null) return;
            var newMark = new MarkData
            {
                TemplateName = $"Mark{MarkDatas.Count + 1}",
                BasePos = new MPoint(),
                CameraPos = new MPoint()
            };
            MarkDatas.Add(newMark);
            SelectedMarkData = newMark;
        }

        private void DeleteMark(object obj)
        {
            if (SelectedMarkData != null && MarkDatas != null)
            {
                MarkDatas.Remove(SelectedMarkData);
            }
        }

        private void TeachMark(object obj)
        {
            if (SelectedMarkData == null) return;

            var device = _hardwareManager.CameraAxisManager.TopCameraAxisDevices.FirstOrDefault();
            if (device != null)
            {
                if (SelectedMarkData.BasePos == null) SelectedMarkData.BasePos = new MPoint();
                if (SelectedMarkData.CameraPos == null) SelectedMarkData.CameraPos = new MPoint();

                if (device.AxisX != null) SelectedMarkData.BasePos.X = device.AxisX.State.ActualPos;
                if (device.AxisY != null) SelectedMarkData.BasePos.Y = device.AxisY.State.ActualPos;
                
                if (device.AxisX != null) SelectedMarkData.CameraPos.X = device.AxisX.State.ActualPos;
                if (device.AxisY != null) SelectedMarkData.CameraPos.Y = device.AxisY.State.ActualPos;
            }
        }

        private void MoveToMark(object obj)
        {
            if (SelectedMarkData == null) return;
            var device = _hardwareManager.CameraAxisManager.TopCameraAxisDevices.FirstOrDefault();
            if (device != null)
            {
                // TODO: Implement Safe Move logic using AxisMotion or similar
            }
        }

        private void MarkModel(object obj)
        {
            // TODO: Open Mark Model Dialog
            // Refresh templates after returning from model dialog as new templates might have been created
            RefreshTemplates();
        }

        private void VisionParSet(object obj)
        {
            // TODO: Open Vision Parameter Settings
            CameraSettingsViewModel settingsViewModel=new CameraSettingsViewModel(_windowManager,_hardwareManager);
            settingsViewModel.Initialize(_substrateRecipe.SubtrateMarkParameterInfo.MarkVisionConfig,
                _substrateRecipe.SubtrateMarkParameterInfo.MarkLightConfig, Common.CameraType.TopPositionCamera);
            _windowManager.ShowDialog(settingsViewModel);
        }

        private void GenerateCoordinates(object obj)
        {
            if (!EnsureRecipeSelected())
            {
                return;
            }

            SyncCoordinateGenerationSource();
            var coordinateState = CoordinateGenerationState;
            if (coordinateState == null)
            {
                return;
            }

            if (coordinateState.Rows <= 0 || coordinateState.Cols <= 0)
            {
                MwMessageBox.Show("基板信息中的行列数必须大于 0。", "生成失败", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (coordinateState.Cols > 1 && Math.Abs(coordinateState.PitchX) <= double.Epsilon)
            {
                MwMessageBox.Show("基板信息中的 Pitch X 无效，无法生成点位。", "生成失败", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            if (coordinateState.Rows > 1 && Math.Abs(coordinateState.PitchY) <= double.Epsilon)
            {
                MwMessageBox.Show("基板信息中的 Pitch Y 无效，无法生成点位。", "生成失败", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var generatedPoints = new ObservableCollection<MarkCoordinatePoint>();
            var index = 1;
            for (var row = 1; row <= coordinateState.Rows; row++)
            {
                for (var col = 1; col <= coordinateState.Cols; col++)
                {
                    generatedPoints.Add(new MarkCoordinatePoint
                    {
                        Index = index++,
                        PointName = CreatePointName(row, col),
                        Row = row,
                        Col = col,
                        TheoryX = (col - 1) * coordinateState.PitchX,
                        TheoryY = (row - 1) * coordinateState.PitchY,
                    });
                }
            }

            coordinateState.Points = generatedPoints;
            NotifyOfPropertyChange(() => CoordinatePreviewPoints);
        }

        private void ImportCoordinates(object obj)
        {
            if (!EnsureRecipeSelected())
            {
                return;
            }

            var dialog = new OpenFileDialog
            {
                Title = "导入坐标文件",
                Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*",
                CheckFileExists = true,
                Multiselect = false,
            };

            if (dialog.ShowDialog() != true)
            {
                return;
            }

            List<MarkCoordinatePoint> importedPoints;
            string errorMessage;
            if (!TryReadCoordinateFile(dialog.FileName, out importedPoints, out errorMessage))
            {
                MwMessageBox.Show(errorMessage, "导入失败", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (importedPoints.Count == 0)
            {
                MwMessageBox.Show("导入文件中没有可用的坐标数据。", "导入失败", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            SyncCoordinateGenerationSource();
            var validationMessage = BuildImportValidationMessage(importedPoints);
            if (!string.IsNullOrWhiteSpace(validationMessage))
            {
                var confirmResult = MwMessageBox.Show(
                    validationMessage + Environment.NewLine + Environment.NewLine + "是否继续导入？",
                    "导入校验",
                    MessageBoxButton.YesNo,
                    MessageBoxImage.Warning,
                    MessageBoxResult.No);

                if (confirmResult != MessageBoxResult.Yes)
                {
                    return;
                }
            }

            CoordinateGenerationState.Points = new ObservableCollection<MarkCoordinatePoint>(importedPoints);
            NotifyOfPropertyChange(() => CoordinatePreviewPoints);
        }

        private void ClearCoordinates(object obj)
        {
            if (!EnsureRecipeSelected() || CoordinateGenerationState == null)
            {
                return;
            }

            CoordinateGenerationState.Points.Clear();
            NotifyOfPropertyChange(() => CoordinatePreviewPoints);
        }

        private void ShowCoordinatePreview(object obj)
        {
            if (!EnsureRecipeSelected())
            {
                return;
            }

            if (_coordinatePreviewWindow != null)
            {
                _coordinatePreviewWindowModel?.RefreshViewState();

                if (_coordinatePreviewWindow.WindowState == WindowState.Minimized)
                {
                    _coordinatePreviewWindow.WindowState = WindowState.Normal;
                }

                _coordinatePreviewWindow.Activate();
                _coordinatePreviewWindow.Focus();
                return;
            }

            _coordinatePreviewWindowModel = new MarkCoordinatePreviewWindowModel(this);
            _coordinatePreviewWindow = new MarkCoordinatePreviewWindow
            {
                DataContext = _coordinatePreviewWindowModel
            };

            var ownerWindow = Application.Current?.Windows
                .OfType<Window>()
                .FirstOrDefault(window => window.IsActive && window != _coordinatePreviewWindow);
            if (ownerWindow != null)
            {
                _coordinatePreviewWindow.Owner = ownerWindow;
            }

            _coordinatePreviewWindow.Closed += OnCoordinatePreviewWindowClosed;
            _coordinatePreviewWindow.Show();
            _coordinatePreviewWindow.Activate();
        }

        private void OnCoordinatePreviewWindowClosed(object sender, EventArgs e)
        {
            if (_coordinatePreviewWindow != null)
            {
                _coordinatePreviewWindow.Closed -= OnCoordinatePreviewWindowClosed;
            }

            _coordinatePreviewWindow = null;
            _coordinatePreviewWindowModel = null;
        }

        private string BuildImportValidationMessage(IReadOnlyList<MarkCoordinatePoint> importedPoints)
        {
            var coordinateState = CoordinateGenerationState;
            if (coordinateState == null)
            {
                return string.Empty;
            }

            var importedRows = importedPoints.Select(point => point.Row).Distinct().Count();
            var importedCols = importedPoints.Select(point => point.Col).Distinct().Count();
            var importedPitchX = CalculatePitch(importedPoints, true);
            var importedPitchY = CalculatePitch(importedPoints, false);

            var mismatchMessages = new List<string>();
            if (coordinateState.Rows > 0 && importedRows != coordinateState.Rows)
            {
                mismatchMessages.Add($"行数不匹配：基板信息为 {coordinateState.Rows}，导入数据为 {importedRows}。");
            }

            if (coordinateState.Cols > 0 && importedCols != coordinateState.Cols)
            {
                mismatchMessages.Add($"列数不匹配：基板信息为 {coordinateState.Cols}，导入数据为 {importedCols}。");
            }

            if (coordinateState.Cols > 1 && importedPitchX.HasValue && Math.Abs(importedPitchX.Value - coordinateState.PitchX) > PitchTolerance)
            {
                mismatchMessages.Add($"Pitch X 不匹配：基板信息为 {coordinateState.PitchX:F4}，导入数据为 {importedPitchX.Value:F4}。");
            }

            if (coordinateState.Rows > 1 && importedPitchY.HasValue && Math.Abs(importedPitchY.Value - coordinateState.PitchY) > PitchTolerance)
            {
                mismatchMessages.Add($"Pitch Y 不匹配：基板信息为 {coordinateState.PitchY:F4}，导入数据为 {importedPitchY.Value:F4}。");
            }

            return mismatchMessages.Count == 0 ? string.Empty : string.Join(Environment.NewLine, mismatchMessages);
        }

        private static double? CalculatePitch(IReadOnlyList<MarkCoordinatePoint> points, bool isXAxis)
        {
            var pitchSamples = new List<double>();
            if (points == null || points.Count == 0)
            {
                return null;
            }

            if (isXAxis)
            {
                foreach (var rowGroup in points.GroupBy(point => point.Row))
                {
                    var ordered = rowGroup.OrderBy(point => point.Col).ToList();
                    for (var i = 1; i < ordered.Count; i++)
                    {
                        var colDelta = ordered[i].Col - ordered[i - 1].Col;
                        if (colDelta <= 0)
                        {
                            continue;
                        }

                        var pitch = Math.Abs((ordered[i].TheoryX - ordered[i - 1].TheoryX) / colDelta);
                        if (pitch > double.Epsilon)
                        {
                            pitchSamples.Add(pitch);
                        }
                    }
                }
            }
            else
            {
                foreach (var colGroup in points.GroupBy(point => point.Col))
                {
                    var ordered = colGroup.OrderBy(point => point.Row).ToList();
                    for (var i = 1; i < ordered.Count; i++)
                    {
                        var rowDelta = ordered[i].Row - ordered[i - 1].Row;
                        if (rowDelta <= 0)
                        {
                            continue;
                        }

                        var pitch = Math.Abs((ordered[i].TheoryY - ordered[i - 1].TheoryY) / rowDelta);
                        if (pitch > double.Epsilon)
                        {
                            pitchSamples.Add(pitch);
                        }
                    }
                }
            }

            return pitchSamples.Count == 0 ? (double?)null : pitchSamples.Average();
        }

        private static string CreatePointName(int row, int col)
        {
            return $"R{row:D2}C{col:D2}";
        }

        private static bool TryReadCoordinateFile(string filePath, out List<MarkCoordinatePoint> importedPoints, out string errorMessage)
        {
            importedPoints = new List<MarkCoordinatePoint>();
            errorMessage = string.Empty;

            if (!File.Exists(filePath))
            {
                errorMessage = "坐标文件不存在。";
                return false;
            }

            var rawLines = File.ReadAllLines(filePath, Encoding.UTF8)
                .Where(line => !string.IsNullOrWhiteSpace(line))
                .ToList();

            if (rawLines.Count == 0)
            {
                errorMessage = "坐标文件为空。";
                return false;
            }

            var headers = SplitCsvLine(rawLines[0]);
            var nameIndex = FindColumnIndex(headers, "pointname", "name", "点位名称", "点位名", "point");
            var rowIndex = FindColumnIndex(headers, "row", "行");
            var colIndex = FindColumnIndex(headers, "col", "column", "列");
            var xIndex = FindColumnIndex(headers, "theoryx", "x", "理论x");
            var yIndex = FindColumnIndex(headers, "theoryy", "y", "理论y");

            if (rowIndex < 0 || colIndex < 0 || xIndex < 0 || yIndex < 0)
            {
                errorMessage = "CSV 需至少包含 Row、Col、TheoryX、TheoryY 列。";
                return false;
            }

            for (var lineIndex = 1; lineIndex < rawLines.Count; lineIndex++)
            {
                var cells = SplitCsvLine(rawLines[lineIndex]);
                if (cells.Count == 0)
                {
                    continue;
                }

                int row;
                int col;
                double theoryX;
                double theoryY;
                if (!TryParseInt(GetCell(cells, rowIndex), out row)
                    || !TryParseInt(GetCell(cells, colIndex), out col)
                    || !TryParseDouble(GetCell(cells, xIndex), out theoryX)
                    || !TryParseDouble(GetCell(cells, yIndex), out theoryY))
                {
                    errorMessage = $"第 {lineIndex + 1} 行数据格式无效，请检查 Row、Col、TheoryX、TheoryY。";
                    return false;
                }

                var pointName = nameIndex >= 0 ? GetCell(cells, nameIndex) : string.Empty;
                importedPoints.Add(new MarkCoordinatePoint
                {
                    PointName = string.IsNullOrWhiteSpace(pointName) ? CreatePointName(row, col) : pointName,
                    Row = row,
                    Col = col,
                    TheoryX = theoryX,
                    TheoryY = theoryY,
                });
            }

            importedPoints = importedPoints
                .OrderBy(point => point.Row)
                .ThenBy(point => point.Col)
                .ToList();

            for (var i = 0; i < importedPoints.Count; i++)
            {
                importedPoints[i].Index = i + 1;
            }

            return true;
        }

        private static int FindColumnIndex(IReadOnlyList<string> headers, params string[] candidates)
        {
            for (var index = 0; index < headers.Count; index++)
            {
                var normalizedHeader = NormalizeHeader(headers[index]);
                if (candidates.Any(candidate => normalizedHeader == NormalizeHeader(candidate)))
                {
                    return index;
                }
            }

            return -1;
        }

        private static string NormalizeHeader(string value)
        {
            return (value ?? string.Empty)
                .Trim()
                .Trim('\uFEFF')
                .Replace(" ", string.Empty)
                .Replace("_", string.Empty)
                .Replace("-", string.Empty)
                .ToLowerInvariant();
        }

        private static string GetCell(IReadOnlyList<string> cells, int index)
        {
            if (index < 0 || index >= cells.Count)
            {
                return string.Empty;
            }

            return cells[index]?.Trim() ?? string.Empty;
        }

        private static bool TryParseInt(string value, out int result)
        {
            if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result))
            {
                return true;
            }

            return int.TryParse(value, NumberStyles.Integer, CultureInfo.CurrentCulture, out result);
        }

        private static bool TryParseDouble(string value, out double result)
        {
            if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result))
            {
                return true;
            }

            return double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out result);
        }

        private static List<string> SplitCsvLine(string line)
        {
            var cells = new List<string>();
            if (line == null)
            {
                return cells;
            }

            var builder = new StringBuilder();
            var inQuotes = false;
            for (var i = 0; i < line.Length; i++)
            {
                var current = line[i];
                if (current == '"')
                {
                    if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
                    {
                        builder.Append('"');
                        i++;
                    }
                    else
                    {
                        inQuotes = !inQuotes;
                    }

                    continue;
                }

                if (current == ',' && !inQuotes)
                {
                    cells.Add(builder.ToString());
                    builder.Clear();
                    continue;
                }

                builder.Append(current);
            }

            cells.Add(builder.ToString());
            return cells;
        }

        #endregion
    }
}
