using System; using System.Collections.Generic; using System.Linq; using StarfaceOutlookSync.Models; namespace StarfaceOutlookSync.Services { /// /// Feldweises 3-Wege-Merge zweier Kontaktstaende gegen ihre Baseline. /// Erlaubt es, dass zwei Stellen unterschiedliche Felder desselben Kontakts /// aendern, ohne dass eine Aenderung verloren geht. Nur wenn DASSELBE Feld /// auf beiden Seiten geaendert wurde, greift die Vorrang-Regel. /// public static class ContactMerger { private class FieldDef { public string Key; public string Label; public Func Get; public Action Set; public bool IsPhone; } private static readonly FieldDef[] Fields = new[] { new FieldDef { Key = "FirstName", Label = "Vorname", Get = c => c.FirstName, Set = (c, v) => c.FirstName = v }, new FieldDef { Key = "LastName", Label = "Nachname", Get = c => c.LastName, Set = (c, v) => c.LastName = v }, new FieldDef { Key = "Company", Label = "Firma", Get = c => c.Company, Set = (c, v) => c.Company = v }, new FieldDef { Key = "JobTitle", Label = "Position", Get = c => c.JobTitle, Set = (c, v) => c.JobTitle = v }, new FieldDef { Key = "Email", Label = "E-Mail", Get = c => c.Email, Set = (c, v) => c.Email = v }, new FieldDef { Key = "EmailSecondary", Label = "E-Mail 2", Get = c => c.EmailSecondary, Set = (c, v) => c.EmailSecondary = v }, new FieldDef { Key = "PhoneWork", Label = "Telefon", Get = c => c.PhoneWork, Set = (c, v) => c.PhoneWork = v, IsPhone = true }, new FieldDef { Key = "PhoneMobile", Label = "Mobil", Get = c => c.PhoneMobile, Set = (c, v) => c.PhoneMobile = v, IsPhone = true }, new FieldDef { Key = "PhoneHome", Label = "Telefon privat", Get = c => c.PhoneHome, Set = (c, v) => c.PhoneHome = v, IsPhone = true }, new FieldDef { Key = "Fax", Label = "Fax", Get = c => c.Fax, Set = (c, v) => c.Fax = v, IsPhone = true }, new FieldDef { Key = "Street", Label = "Strasse", Get = c => c.Street, Set = (c, v) => c.Street = v }, new FieldDef { Key = "City", Label = "Ort", Get = c => c.City, Set = (c, v) => c.City = v }, new FieldDef { Key = "PostalCode", Label = "PLZ", Get = c => c.PostalCode, Set = (c, v) => c.PostalCode = v }, new FieldDef { Key = "State", Label = "Bundesland", Get = c => c.State, Set = (c, v) => c.State = v }, new FieldDef { Key = "Country", Label = "Land", Get = c => c.Country, Set = (c, v) => c.Country = v }, new FieldDef { Key = "Website", Label = "Webseite", Get = c => c.Website, Set = (c, v) => c.Website = v }, new FieldDef { Key = "Notes", Label = "Notizen", Get = c => c.Notes, Set = (c, v) => c.Notes = v }, new FieldDef { Key = "Salutation", Label = "Anrede", Get = c => c.Salutation, Set = (c, v) => c.Salutation = v }, new FieldDef { Key = "Title", Label = "Titel", Get = c => c.Title, Set = (c, v) => c.Title = v }, new FieldDef { Key = "Birthday", Label = "Geburtstag", Get = c => c.Birthday, Set = (c, v) => c.Birthday = v }, }; /// Liest den Wert eines Feldes per stabilem Schluessel. public static string GetValue(UnifiedContact c, string key) { if (c == null || string.IsNullOrEmpty(key)) return ""; var f = Fields.FirstOrDefault(x => x.Key == key); return f == null ? "" : (f.Get(c) ?? ""); } /// Vergleicht zwei Feldwerte (telefon-normalisiert je nach Feld). public static bool ValuesEqual(string key, string a, string b) { var f = Fields.FirstOrDefault(x => x.Key == key); return Equal(a ?? "", b ?? "", f?.IsPhone ?? false); } /// /// Fuehrt den Outlook- und Starface-Stand gegen ihre jeweilige Baseline /// zusammen. outlookWins entscheidet bei echten Feld-Konflikten. /// public static (UnifiedContact merged, List conflicts) Merge( UnifiedContact baseOutlook, UnifiedContact baseStarface, UnifiedContact outlook, UnifiedContact starface, bool outlookWins) { var merged = new UnifiedContact { OutlookEntryId = outlook.OutlookEntryId, StarfaceId = starface.StarfaceId }; var conflicts = new List(); foreach (var f in Fields) { string olv = f.Get(outlook) ?? ""; string sfv = f.Get(starface) ?? ""; string bol = f.Get(baseOutlook) ?? ""; string bsf = f.Get(baseStarface) ?? ""; bool olChanged = !Equal(olv, bol, f.IsPhone); bool sfChanged = !Equal(sfv, bsf, f.IsPhone); string chosen; if (olChanged && !sfChanged) { chosen = olv; } else if (sfChanged && !olChanged) { chosen = sfv; } else if (olChanged && sfChanged) { if (Equal(olv, sfv, f.IsPhone)) { // Beide auf denselben Wert geaendert -> kein echter Konflikt. chosen = olv; } else { chosen = outlookWins ? olv : sfv; conflicts.Add(new FieldConflict { StarfaceId = starface.StarfaceId, ContactName = outlook.DisplayName, Field = f.Label, FieldKey = f.Key, OutlookValue = olv, StarfaceValue = sfv, Winner = outlookWins ? "Outlook" : "Starface" }); } } else { // Keine Seite hat dieses Feld geaendert -> Outlook-Wert behalten. chosen = olv; } f.Set(merged, chosen); } return (merged, conflicts); } private static bool Equal(string a, string b, bool isPhone) { if (isPhone) return NormalizePhone(a) == NormalizePhone(b); return string.Equals(a ?? "", b ?? "", StringComparison.OrdinalIgnoreCase); } private static string NormalizePhone(string phone) { if (string.IsNullOrEmpty(phone)) return ""; return new string(phone.Where(c => char.IsDigit(c) || c == '+').ToArray()); } } }