UserControls/PatternDgvControl.cs

/*
 * Copyright XXXX Co.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using ChatworkBulkSender.Daos;
using ChatworkBulkSender.Dtos;
using ChatworkBulkSender.Utils;
using ChatworkBulkSender.Forms;
using System.Linq;

namespace ChatworkBulkSender.UserControls
{
    [DesignerCategory("UserControl")] // デザイナーでユーザーコントロールとして表示することを明記する
    public partial class PattrernDgvControl : BasePatternMasterDgvControl
    {
        protected override int EditButtonColumnIndex => 4;  // 編集ボタンは5列目(インデックス4)
        protected override int SortableColumnMaxIndex => 3;  // ソート可能な最大列(未使用まで)
        protected override string InitialSortColumnName => "SortOrder";

        EventHandler<PatternMasterDto> _createdSendPattern;

        public PattrernDgvControl()
        {
            if (!IsInDesignMode())
            {
                InitializeComponent();
                if (_dgv == null)
                {
                    base._dgv = this.patternDgv;
                }
                InitializeDgv();
                SetUpColumns();
                patternDgv.Dock = DockStyle.Fill;

            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (!IsInDesignMode())
            {
                _createdSendPattern = (s, args) => CreatedSendPattern();
                _individualEditForm = new M_SendPatternIndividualEdit();
                _individualEditForm.DataCreated += _createdSendPattern;
                RefreshData();
            }

        }

        protected override void InitializeDgv()
        {
            base.InitializeDgv();
        }

        private void CreatedSendPattern()
        {
            RefreshData();
        }

        /// <summary>
        /// 有効なパターンをDGVに読み込む
        /// </summary>
        private void SetUpColumns()
        {
            if (_dgv == null) { return; }

            _dgv.Columns.Clear();

            // パターン名称
            var patternName = new DataGridViewTextBoxColumn
            {
                DataPropertyName = nameof(PatternMasterDto.PatternName),
                Name = nameof(PatternMasterDto.PatternName),
                HeaderText = "パターン名称",
                ReadOnly = true,
                SortMode = DataGridViewColumnSortMode.Programmatic,
            };
            patternName.FillWeight = 300;
            _dgv.Columns.Add(patternName);

            // 定型文
            var templateText = new DataGridViewTextBoxColumn
            {
                DataPropertyName = nameof(PatternMasterDto.TemplateText),
                Name = nameof(PatternMasterDto.TemplateText),
                HeaderText = "定型文",
                ReadOnly = true,
                SortMode = DataGridViewColumnSortMode.Programmatic,
            };
            templateText.FillWeight = 500;
            _dgv.Columns.Add(templateText);

            // 並び順
            var sortOrderCol = new DataGridViewTextBoxColumn
            {
                DataPropertyName = nameof(PatternMasterDto.SortOrder),
                Name = nameof(PatternMasterDto.SortOrder),
                HeaderText = "並び順",
                ReadOnly = true,
                SortMode = DataGridViewColumnSortMode.Programmatic,
            };
            sortOrderCol.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
            sortOrderCol.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
            sortOrderCol.HeaderCell.Style.Padding = new Padding(12, 0, 0, 0);
            sortOrderCol.FillWeight = 50;
            _dgv.Columns.Add(sortOrderCol);

            // 未使用フラグ
            var isUnusedCol = new DataGridViewCheckBoxColumn
            {
                DataPropertyName = nameof(PatternMasterDto.IsUnused),
                Name = nameof(PatternMasterDto.IsUnused),
                HeaderText = "未使用フラグ",
                ReadOnly = true,
                SortMode = DataGridViewColumnSortMode.Programmatic,
            };
            isUnusedCol.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
            isUnusedCol.HeaderCell.Style.Padding = new Padding(12, 0, 0, 0);
            isUnusedCol.FillWeight = 70;
            _dgv.Columns.Add(isUnusedCol);

            // 編集ボタン
            var editCol = new DataGridViewButtonColumn
            {
                Name = "BtnEdit",
                HeaderText = "編集",
                Text = "✏",
                UseColumnTextForButtonValue = true,
                ReadOnly = true,
            };
            editCol.DefaultCellStyle.Font = new Font("Segoe UI Emoji", 10);
            editCol.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
            editCol.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
            editCol.FillWeight = 50;
            _dgv.Columns.Add(editCol);

        }

        protected override void ShowEditForm(PatternMasterDto selectedItem)
        {
            _dgv.Enabled = false;

            try
            {
                if (_individualEditForm != null && !_individualEditForm.IsDisposed)
                {
                    var form = _individualEditForm;

                    if (form != null)
                    {
                        form.DataCreated += _createdSendPattern;
                    }
                    _individualEditForm.Close();
                    _individualEditForm.Dispose();
                    // 内部メッセージ処理
                    Application.DoEvents();
                }

                // 編集前に最新のデータを取得して更新チェック
                var dao = new PatternMasterDao();
                var latestData = dao.GetById(selectedItem.PatternId);
                
                if (latestData == null)
                {
                    MessageBox.Show("対象のデータが削除されています。一覧を更新します。", "エラー", 
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    _dgv.Enabled = true;  // DataGridViewを再度有効化
                    RefreshData();
                    return;
                }
                
                // 元の更新日時を取得(データロード時に保存された値)
                var originalUpdateTime = GetOriginalUpdateTime(selectedItem);
                
                // 更新日時が異なる場合(元の値と最新値を比較)
                // DateTime精度問題対策:1秒以内の差は同じとみなす
                bool isUpdatedByOthers = false;
                if (latestData.UpdatedDate.HasValue && originalUpdateTime.HasValue)
                {
                    var timeDiff = Math.Abs((latestData.UpdatedDate.Value - originalUpdateTime.Value).TotalSeconds);
                    isUpdatedByOthers = timeDiff > 1.0;  // 1秒以上の差がある場合のみ更新とみなす
                }
                else if (latestData.UpdatedDate != originalUpdateTime)
                {
                    isUpdatedByOthers = true;
                }
                
                if (isUpdatedByOthers)
                {
                    MessageBox.Show(MessageBoxUtil.DB_011, "エラー", 
                        MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    _dgv.Enabled = true;  // DataGridViewを再度有効化
                    RefreshData();
                    return;
                }
                
                // フォームを作成(更新ボタン配置の設定にする)
                _individualEditForm = new M_SendPatternIndividualEdit(
                    M_SendPatternIndividualEdit.ButtonMode.Update, latestData);

                var editForm = _individualEditForm;
                if (editForm != null)
                {
                    editForm.DataCreated += _createdSendPattern;
                }

                _individualEditForm.Owner = this.ParentForm;

                _individualEditForm.FormClosed += (s, args) =>
                {
                    _dgv.Enabled = true;

                    if (this.ParentForm != null && !this.ParentForm.Visible)
                    {
                        this.ParentForm.Show();
                        this.ParentForm.BringToFront();
                    }

                    RefreshData();
                };

                this.ParentForm.Hide();
                _individualEditForm.Show();

            }
            catch (Exception e)
            {
                _dgv.Enabled = true;

                if (this.ParentForm != null && !this.ParentForm.Visible)
                {
                    this.ParentForm.Show();
                }

                MessageBoxUtil.ShowErr($"個別編集画面の表示中にエラーが発生しました。\n\n{e.Message}");
            }
        }

        protected override List<PatternMasterDto> GetDataFromDao()
        {
            var dao = new PatternMasterDao();

            var currentData = dao.GetAll();

            if (_searchCriteria != null && _searchCriteria.Count > 0)
            {
                var query = currentData.AsQueryable();

                if (_searchCriteria.ContainsKey("PatternName"))
                {
                    var patternName = _searchCriteria["PatternName"].ToString();
                    query = query.Where(x => x.PatternName != null && x.PatternName.Contains(patternName));
                }

                if ((_searchCriteria.ContainsKey("ManagementNumber") && !string.IsNullOrEmpty(_searchCriteria["ManagementNumber"].ToString())) ||
                        (_searchCriteria.ContainsKey("CustomerName") && !string.IsNullOrEmpty(_searchCriteria["CustomerName"].ToString())))
                {
                    var customerDao = new CustomerMasterDao();
                    var allCostomers = customerDao.GetAll(true);
                    var patternCustomerDao = new PatternCustomerMasterDao();
                    var matchingPatternIds = new HashSet<int>();

                    foreach (var pattern in currentData)
                    {
                        var patternCustomers = patternCustomerDao.GetCustomersByPatternId(pattern.PatternId);

                        foreach (var patternCust in patternCustomers)
                        {
                            var customer = allCostomers.FirstOrDefault(c => c.ManagementNumber == patternCust.PatternId);

                            if (customer != null)
                            {
                                bool isMatch = false;


                                // 管理番号(前方一致)
                                if (_searchCriteria.ContainsKey("ManagementNumber") && !string.IsNullOrEmpty(_searchCriteria["ManagementNumber"].ToString()))
                                {
                                    var searchMgmtNumber = _searchCriteria["ManagementNumber"].ToString();
                                    if (customer.ManagementNumber.ToString().StartsWith(searchMgmtNumber))
                                    {
                                        isMatch = true;
                                    }
                                }

                                // 顧客名(部分一致)
                                if (!isMatch && _searchCriteria.ContainsKey("CustomerName") && !string.IsNullOrEmpty(_searchCriteria["CustomerName"].ToString()))
                                {
                                    var searchCustomerName = _searchCriteria["CustomerName"].ToString();
                                    if (customer.CustomerName != null && customer.CustomerName.Contains(searchCustomerName))
                                    {
                                        isMatch = true;
                                    }
                                }

                                if (isMatch)
                                {
                                    matchingPatternIds.Add(pattern.PatternId);
                                    break; // このパターンは既に一致したので次のパターンへ
                                }
                            }
                        }
                    }
                    query = query.Where(x => matchingPatternIds.Contains(x.PatternId));
                }

                // 未使用データ表示
                if (_searchCriteria.ContainsKey("IncludeUnused") && !(bool)_searchCriteria["IncludeUnused"])
                {
                    query = query.Where(x => !x.IsUnused);
                }

                return query.ToList();
            }
            // デフォルトは未使用を除外
            return currentData.Where(x => !x.IsUnused).ToList();
        }

        protected override bool HasUpdateAfter(DateTime? lastLoadTime)
        {
            if (lastLoadTime == null) { return false; }

            return new CustomerMasterDao().HasUpdatesAfter(lastLoadTime);
        }
        public override void Search(Dictionary<string, object> criteria)
        {
            base.Search(criteria);
            this.GetDataFromDao();
        }

        protected override void Dgv_ColumnHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            base.Dgv_ColumnHeaderMouseDoubleClick(sender, e);
        }

        protected override void Dgv_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            base.Dgv_CellClick(sender, e);
        }

        private void customerDgv_BringContextChanged(object sender, EventArgs e)
        {
            ClearSelection();
        }
        private void customerDgv_Leave(object sender, EventArgs e)
        {
            ClearSelection();
        }
        
        /// <summary>
        /// 各パターンDTOの更新日時を保存する
        /// </summary>
        protected override void StoreOriginalUpdateTimes(List<PatternMasterDto> list)
        {
            _originalUpdateTimes.Clear();
            foreach (var dto in list)
            {
                _originalUpdateTimes[dto.PatternId] = dto.UpdatedDate;
            }
        }
        
        /// <summary>
        /// 指定されたパターンDTOの元の更新日時を取得する
        /// </summary>
        protected override DateTime? GetOriginalUpdateTime(PatternMasterDto dto)
        {
            if (_originalUpdateTimes.ContainsKey(dto.PatternId))
            {
                return _originalUpdateTimes[dto.PatternId];
            }
            // 見つからない場合は現在の値を返す(後方互換性のため)
            return dto.UpdatedDate;
        }
        
        /// <summary>
        /// 新規アイテムの更新日時のみを追加(既存アイテムの更新日時は変更しない)
        /// </summary>
        protected override void UpdateOriginalUpdateTimesForNewItems(List<PatternMasterDto> list)
        {
            foreach (var dto in list)
            {
                // 既存のエントリがない場合のみ追加
                if (!_originalUpdateTimes.ContainsKey(dto.PatternId))
                {
                    _originalUpdateTimes[dto.PatternId] = dto.UpdatedDate;
                }
            }
        }

    }
}