using dataladder.Matching;
using DataMatch.Project.Descriptors.MatchDefinition;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SdkInterface
{
    public class Matcher
    {
        private List<ApiSource> _sources;
        private MatchingRules _rules;
        private MatchPairs _pairs;
        private MatchDefinitionBuilder _matchDefinitionBuilder;
        private MatchEngine _engine;

        public Matcher(string workingFolder, List<ApiSource> list, MatchingRules rules, MatchPairs pairs)
        {
            if (!CheckCorrectnessRules(list, rules, pairs, out string errorMsg))
                throw new ArgumentException($"Matcher constructor: wrong parameters. {errorMsg}");

            _sources = list;
            _rules = rules;
            _pairs = pairs;

            string tmpPath = System.IO.Path.Combine(workingFolder, "Temp");

            _matchDefinitionBuilder = new MatchDefinitionBuilder();
            var mmm = _matchDefinitionBuilder.MultipleMatchDefinitionsManager;
            mmm.AvailableFields = CreateAvailableFields(list, rules);
           


            _engine = new MatchEngine(mmm, rules.AllRecordsInGroupMustBeSimilar, tmpPath, workingFolder, "someName");
            _engine.DoMatch();
        }

        private AvailableFields CreateAvailableFields(List<ApiSource> list, MatchingRules rules)
        {
            throw new NotImplementedException();
        }

        private bool CheckCorrectnessRules(List<ApiSource> list, MatchingRules rules, MatchPairs pairs, out string errorMsg)
        {
            errorMsg = string.Empty;
            return false;
        }

        public DataTable Results { get; set; }

        public void Match()
        {
            _engine.DoMatch();
        }
    }

    public class MatchingRules
    {
        public List<MappedField> MappedFields { get; set; }
        public List<Definition> Definitions { get; set; }
        public Boolean AllRecordsInGroupMustBeSimilar { get; set; }
        public Int32 MaxMatchesForOneRow { get; set; } = 0;
        public Int32 MaxMatchesPerGroup { get; set; } = 5;

        public static MatchingRules RulesFromProjectSpec(MatchDefinitionSpec spec)
        {
            var res = new MatchingRules();
            res.MappedFields = FieldsFromMappedFieldsSpec(spec.MappedFields);
            res.Definitions = DefinitionsFromDefinitionsSpec(spec.Definitions);
            res.AllRecordsInGroupMustBeSimilar = spec.AllRecordsInGoupMustBeSimilar;
            res.MaxMatchesForOneRow = spec.MaxMatchesForOneRow;
            res.MaxMatchesPerGroup = spec.MaxMatchesPerGroup;

            return res;
        }

        private static List<Definition> DefinitionsFromDefinitionsSpec(DefinitionsSpec spec)
        {
            return spec.Definitions.Select(d => ConvertDefinitionSpecToSdkDefinition(d)).ToList();
        }

        private static Definition ConvertDefinitionSpecToSdkDefinition(DefinitionSpec spec)
        {
            Definition res = new Definition();
            res.Criterias = spec.MatchCriteriaList.Select(m => ConvertCriteriaSpecToSdkCriteria(m)).ToList();
            return res;
        }

        private static Criteria ConvertCriteriaSpecToSdkCriteria(MatchCriteriaSpec spec)
        {
            Criteria res = new Criteria();
            res.Fuzzy = spec.Fuzzy;
            res.AddWeightToFirstLetter = spec.AddWeightToFirstLetter;
            res.Exact = spec.Exact;
            res.Numeric = spec.Numeric;
            res.UseMetaphone = spec.UseMetaphone;
            res.IgnoreCase = spec.IgnoreCase;
            res.Level = spec.Level;
            res.GroupLevel = spec.GroupLevel;
            res.MinAllowedLevelInGroup = spec.MinAllowedLevelInGroup;
            res.GroupId = spec.GroupId;
            res.CrossColumnGroupId = spec.CrossColumnGroupId;
            res.MaxTotalWeightBelow = spec.MaxEmptyWeightBelow;
            res.MaxMismatchWeightBelow = spec.MaxMismatchWeightBelow;
            res.MaxEmptyWeightBelow = spec.MaxEmptyWeightBelow;
            res.Weight = spec.Weight;
            res.MatchingIndex = spec.MatchingIndex;
            res.AbsoluteMatchingIndex = spec.AbsoluteMatchingIndex;

            res.TableFieldDictionary = spec.TableFieldDictionary;

            return res;
        }

        private static List<MappedField> FieldsFromMappedFieldsSpec(MappedFieldsSpec mappedFields)
        {
            return mappedFields.MappedFields.Select(sp => new MappedField()
            {
                Include = sp.Include,
                MappedFields = sp.MappedFields
            }).ToList();
        }
    }

    public class Criteria
    {
        public Boolean Fuzzy { get; set; }
        public Boolean AddWeightToFirstLetter { get; set; }
        public Boolean Exact { get; set; }
        public Boolean Numeric { get; set; }
        public Boolean UseMetaphone { get; set; }
        public Boolean IgnoreCase { get; set; } = true;
        public Double Level { get; set; }
        public Double GroupLevel { get; set; }
        public Double MinAllowedLevelInGroup { get; set; }
        public Int32 GroupId { get; set; }
        public Int32 CrossColumnGroupId { get; set; }
        public Int32 MaxTotalWeightBelow { get; set; }
        public Int32 MaxMismatchWeightBelow { get; set; }
        public Int32 MaxEmptyWeightBelow { get; set; }
        public Double Weight { get; set; }
        public Byte MatchingIndex { get; set; }
        public Byte AbsoluteMatchingIndex { get; set; }

        public Dictionary<String, String> TableFieldDictionary { get; set; }
    }

    public class Definition
    {
        public List<Criteria> Criterias { get; set; }
    }

    public class MappedField
    {
        public Boolean Include { get; set; } = true;
        public Dictionary<String, String> MappedFields { get; set; }
    }

    public class MatchPairs
    {
        public List<KeyValuePair<string, string>> Pairs { get; set; }
    }
}
