using KssSmaPlaLib.Ini;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Drawing.Design;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelToCsvToDbCommon.Class
{
    public enum ColumnType 
    {
        Fixed,
        Mapping,
        Conversion,
        HorizontalDate,
        ValueAmount,
        FuncReplace,
        FuncSubstring,
        LineId,
        FileConversion,
        ModuleId,
    }

    [TypeConverter(typeof(ExpandableObjectConverter))] // これで [+] で展開可能に！
    [Editor(typeof(ColumnDefinitionEditor), typeof(UITypeEditor))]
    public class ColumnDefinition
    {
        [ReadOnly(true), DisplayName("論理名")]
        public string LogicalName { get; set; } = string.Empty; // PlanCd, KoteiCdなど
        [ReadOnly(true), DisplayName("タイプ")]
        public ColumnType Type { get; set; }    // 処理タイプ

        public string TypeName
        {
            get
            {
                return GetTypeName(this.Type);
            }
        }

        [ReadOnly(true), DisplayName("列(A-Z)")]
        public string TargetCol { get; set; } = string.Empty;   // B, C, ANなどの列レター
        [ReadOnly(true), DisplayName("固定値/条件")]
        public string FixedValue { get; set; } = string.Empty;  // 固定値 or 変換条件など

        [ReadOnly(true), DisplayName("変換表")]
        public Dictionary<string, string> ConvertMap { get; set; } // 変換テーブル

        public string Comment { get; set; } = string.Empty;
        public string Naiyo
        {
            get
            {
                switch(this.Type)
                {
                    case ColumnType.Fixed:
                        return $"固定値 \"{this.FixedValue}\" を出力します。";
                    case ColumnType.Mapping:
                        return $"列 [{this.TargetCol}] の値を出力します。";
                    case ColumnType.Conversion:
                        var henkanhyo = string.Join(",", this.ConvertMap.Select(m => m.Key + ":" + m.Value).ToList());
                        return $"列 [{this.TargetCol}] の値を変換して出力します。（{henkanhyo}）";
                    case ColumnType.FuncReplace:
                        return $"列 [{this.TargetCol}] の値を部分置換して出力します。（{this.FixedValue}）";
                    case ColumnType.FuncSubstring:
                        return $"列 [{this.TargetCol}] の値を部分切り取りして出力します。（{this.FixedValue.Split(',')[0]}桁目から{this.FixedValue.Split(',')[1]}桁）";
                    case ColumnType.HorizontalDate:
                        return $"横並びのカレンダ日付を出力します。";
                    case ColumnType.ValueAmount:
                        return $"その日の払出し数を出力します。";
                    default:
                        return $"(未登録)";
                }
            }
        }

        public string IniRaw()
        {
            var raw = this.LogicalName + ":";
            switch (this.Type)
            {
                case ColumnType.Fixed:
                    raw += $"\"{this.FixedValue}\"";
                    break;
                case ColumnType.Mapping:
                    raw += this.TargetCol;
                    break;
                case ColumnType.Conversion:
                    var kvpCsv = string.Join(",", this.ConvertMap.Select(m => m.Key + "=" + m.Value).ToList());
                    raw += "変換値," + this.TargetCol + "," + kvpCsv;
                    break;
                case ColumnType.HorizontalDate:
                    raw += "横日付";
                    break;
                case ColumnType.ValueAmount:
                    raw += "数";
                    break;
                case ColumnType.FuncReplace:
                    raw += "Replace(" + this.TargetCol + "," + this.FixedValue.Replace("→",",") + ")";
                    break;
                case ColumnType.FuncSubstring:
                    raw += "Substring(" + this.TargetCol + "," + this.FixedValue + ")";
                    break;
            }
            return raw;
        }

        // PropertyGrid上に「何の設定か」を一言で表示する
        public override string ToString() => $"{LogicalName} ({Type})";

        public static string GetCsvHeader(List<ColumnDefinition> cdList)
        {
            // ガード句：リストが空なら空文字を返す
            if (cdList == null || cdList.Count == 0) return string.Empty;

            // LogicalNameをカンマで連結して一行のヘッダー文字列を作成
            return string.Join(",", cdList.Select(c => c.LogicalName));
        }

        public static List<ColumnDefinition> Load(string iniPath, string sectionName)
        {
            var cdList = new List<ColumnDefinition>();
            for (int cnt = 1; cnt < 1000; cnt++)
            {
                var rawValue = IniHelper.ReadValue(iniPath, sectionName, $"Col{cnt:d3}", string.Empty);
                if (string.IsNullOrEmpty(rawValue)) break;
                cdList.Add(Parse(rawValue, out var messageForMe));
            }
            var comments = IniHelper.ReadValue(iniPath, sectionName, "ColComments", string.Empty);
            if (!string.IsNullOrEmpty(comments))
            {
                var ary = comments.Split(',');
                for (var cnt = 0; cnt < Math.Min(cdList.Count, ary.Length); cnt++)
                {
                    cdList[cnt].Comment = ary[cnt].Replace("@@@COMMA@@@", ",");
                }
            }
            return cdList;
        }

        /// <summary>
        /// INI記載値のバリデとnew
        /// </summary>
        /// <param name="rawValue"></param>
        /// <param name="resultMessage"></param>
        /// <returns></returns>
        public static ColumnDefinition Parse(string rawValue,out string resultMessage)
        {
            resultMessage = string.Empty;
            try
            {
                // 例: Col001=PlanCd:"K*SP**" -> LogicalName="PlanCd", body="\"K*SP**\""
                var parts = rawValue.Split(new[] { ':' }, 2);
                var def = new ColumnDefinition { LogicalName = parts[0] };
                string body = parts[1];

                // 1. 固定値判定 (ダブルクォーテーションで囲まれている)
                if (body.StartsWith("\"") && body.EndsWith("\""))
                {
                    def.Type = ColumnType.Fixed;
                    def.FixedValue = body.Trim('\"');
                }
                // 1.5. ラインID
                else if (body == "ラインID")
                {
                    def.Type = ColumnType.LineId;
                }
                // 1.6. モジュールID
                else if (body == "モジュールID")
                {
                    def.Type = ColumnType.ModuleId;
                }
                // 2. 関数判定 (Replaceなどが含まれる)
                else if (body.Contains("Replace("))
                {
                    def.Type = ColumnType.FuncReplace;
                    // 後で正規表現などでANや"-"を引っこ抜く
                    var ary = (body.Replace("Replace(", "").Trim(')').Trim() + ",,").Split(',');
                    def.TargetCol = ary[0].Trim();
                    def.FixedValue = ary[1].Trim() + "→" + ary[2].Trim();
                }
                else if (body.Contains("Substring("))
                {
                    def.Type = ColumnType.FuncSubstring;
                    // 後で正規表現などでANや"-"を引っこ抜く
                    var ary = (body.Replace("Substring(", "").Trim(')').Trim() + ",,").Split(',');
                    def.TargetCol = ary[0].Trim();
                    def.FixedValue = ary[1].Trim() + "," + ary[2].Trim();
                }
                // 3. 変換値判定 (カンマ区切りで"変換値"とある)
                else if (body.StartsWith("変換値"))
                {
                    def.Type = ColumnType.Conversion;
                    var csv = body.Split(',');
                    def.TargetCol = csv[1]; // B列など
                    def.ConvertMap = csv.Skip(2).Select(s => s.Split('=')).ToDictionary(a => a[0], a => a[1]);
                }
                // 3.1 ファイル変換値判定 (カンマ区切りで"ファイル変換値"とある)
                else if (body.StartsWith("ファイル変換値"))
                {
                    def.Type = ColumnType.FileConversion;
                    var csv = body.Split(',');
                    def.TargetCol = csv[1]; // B列など
                    var path = csv[2];
                    if (path.IndexOf("\\") < 0) path = ".\\" + path;
                    var dic = new Dictionary<string, string>();
                    if (!string.IsNullOrEmpty(path) && File.Exists(path))
                    {
                        using (var sr = new StreamReader(path))
                        {
                            var all = sr.ReadToEnd();
                            var lines = all.Replace("\r\n", "\n").Replace("\r", "\n").Split('\n');
                            foreach (var line in lines)
                            {
                                if (string.IsNullOrEmpty(line)) continue;
                                var items = line.Split('\t');
                                dic.Add(items[0], items[1]);
                            }
                        }
                    }
                    def.ConvertMap = dic;
                }
                // 4. 特殊主役判定 (横日付・数)
                else if (body == "横日付") def.Type = ColumnType.HorizontalDate;
                else if (body == "数") def.Type = ColumnType.ValueAmount;
                // 4.1 特殊主役判定 (数(...))
                else if (body.StartsWith("数("))
                {
                    def.Type = ColumnType.ValueAmount;
                    var csv = body.Substring(2).TrimEnd(')').Split(',');
                    def.TargetCol = csv[0].Trim();
                    def.FixedValue = csv[1].Trim();
                }
                // 5. 単純マッピング
                else
                {
                    def.Type = ColumnType.Mapping;
                    def.TargetCol = body; // D列など
                }
                return def;
            }
            catch (Exception ex)
            {
                resultMessage = ex.Message;
                return null;
            }
        }
        public static string GetTypeName(ColumnType type)
        {
            switch (type)
            {
                case ColumnType.Fixed: return "固定値";
                case ColumnType.Mapping: return "セル値";
                case ColumnType.Conversion: return "変換値";
                case ColumnType.FuncReplace: return "置換値";
                case ColumnType.FuncSubstring: return "部分切り取り値";
                case ColumnType.HorizontalDate: return "横日付";
                case ColumnType.ValueAmount: return "数";
                case ColumnType.LineId: return "ラインID";
                case ColumnType.ModuleId: return "モジュールID";
                default: return "(設定なし)";
            }
        }
    }
}
