using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using StarfaceOutlookSync.Models; namespace StarfaceOutlookSync.Services { public class SyncEngine { private readonly ProfileManager _profileManager = new ProfileManager(); private readonly OutlookContactsService _outlookService = new OutlookContactsService(); public event Action OnProgress; private void Log(string message) => OnProgress?.Invoke(message); private static UnifiedContact FindMatch(UnifiedContact contact, List candidates) { // Erst E-Mail-Match if (!string.IsNullOrEmpty(contact.Email)) { var byEmail = candidates.FirstOrDefault(c => !string.IsNullOrEmpty(c.Email) && c.Email.Equals(contact.Email, StringComparison.OrdinalIgnoreCase)); if (byEmail != null) return byEmail; } // Dann Name-Match if (!string.IsNullOrEmpty(contact.FirstName) || !string.IsNullOrEmpty(contact.LastName)) { var byName = candidates.FirstOrDefault(c => c.FirstName.Equals(contact.FirstName, StringComparison.OrdinalIgnoreCase) && c.LastName.Equals(contact.LastName, StringComparison.OrdinalIgnoreCase) && (!string.IsNullOrEmpty(c.FirstName) || !string.IsNullOrEmpty(c.LastName))); if (byName != null) return byName; } return null; } public async Task SyncProfileAsync(SyncProfile profile) { var result = new SyncResult { ProfileName = profile.Name, Timestamp = DateTime.Now.ToString("o") }; try { // Starface verbinden Log("Verbinde mit Starface..."); using (var starface = new StarfaceApiClient(profile.StarfaceConnection)) { starface.OnDebug += (msg) => Log(msg); var loginOk = await starface.LoginAsync(); if (!loginOk) { result.ErrorMessages.Add("Starface-Login fehlgeschlagen"); result.Errors++; return result; } var mappings = _profileManager.GetMappings(profile.Id); var mappingByOutlook = mappings.ToDictionary(m => m.OutlookEntryId, m => m); var mappingByStarface = mappings.ToDictionary(m => m.StarfaceId, m => m); // Kontakte laden Log("Lade Outlook-Kontakte..."); var outlookContacts = _outlookService.GetContacts(profile.OutlookFolderPath); Log($"{outlookContacts.Count} Outlook-Kontakte geladen"); Log("Lade Starface-Kontakte..."); var starfaceContacts = await starface.GetContactsAsync(profile.StarfaceAddressBook); Log($"{starfaceContacts.Count} Starface-Kontakte geladen"); // Outlook -> Starface if (profile.SyncDirection == SyncDirection.Both || profile.SyncDirection == SyncDirection.OutlookToStarface) { Log("Synchronisiere Outlook -> Starface..."); foreach (var oc in outlookContacts) { try { SyncMapping existing = null; if (!string.IsNullOrEmpty(oc.OutlookEntryId)) mappingByOutlook.TryGetValue(oc.OutlookEntryId, out existing); if (existing != null) { var hash = oc.GetHash(); if (hash != existing.LastSyncHash) { if (await starface.UpdateContactAsync(existing.StarfaceId, oc, profile.StarfaceAddressBook)) { existing.LastSyncHash = hash; result.Updated++; } } } else { var match = FindMatch(oc, starfaceContacts); if (match != null && !string.IsNullOrEmpty(match.StarfaceId)) { if (await starface.UpdateContactAsync(match.StarfaceId, oc, profile.StarfaceAddressBook)) { _profileManager.AddOrUpdateMapping(new SyncMapping { ProfileId = profile.Id, OutlookEntryId = oc.OutlookEntryId, StarfaceId = match.StarfaceId, LastSyncHash = oc.GetHash() }); result.Updated++; } } else { var created = await starface.CreateContactAsync(oc, profile.StarfaceAddressBook); if (created != null && !string.IsNullOrEmpty(created.StarfaceId)) { _profileManager.AddOrUpdateMapping(new SyncMapping { ProfileId = profile.Id, OutlookEntryId = oc.OutlookEntryId, StarfaceId = created.StarfaceId, LastSyncHash = oc.GetHash() }); result.Created++; } } } } catch (Exception ex) { result.Errors++; result.ErrorMessages.Add($"{oc.DisplayName}: {ex.Message}"); } } } // Starface -> Outlook if (profile.SyncDirection == SyncDirection.Both || profile.SyncDirection == SyncDirection.StarfaceToOutlook) { Log("Synchronisiere Starface -> Outlook..."); foreach (var sc in starfaceContacts) { try { SyncMapping existing = null; if (!string.IsNullOrEmpty(sc.StarfaceId)) mappingByStarface.TryGetValue(sc.StarfaceId, out existing); if (existing != null) { var hash = sc.GetHash(); if (hash != existing.LastSyncHash) { if (_outlookService.UpdateContact(existing.OutlookEntryId, sc)) { existing.LastSyncHash = hash; result.Updated++; } } } else { var match = FindMatch(sc, outlookContacts); if (match != null && !string.IsNullOrEmpty(match.OutlookEntryId)) { if (_outlookService.UpdateContact(match.OutlookEntryId, sc)) { _profileManager.AddOrUpdateMapping(new SyncMapping { ProfileId = profile.Id, OutlookEntryId = match.OutlookEntryId, StarfaceId = sc.StarfaceId, LastSyncHash = sc.GetHash() }); result.Updated++; } } else { var created = _outlookService.CreateContact(sc, profile.OutlookFolderPath); if (created != null && !string.IsNullOrEmpty(created.OutlookEntryId)) { _profileManager.AddOrUpdateMapping(new SyncMapping { ProfileId = profile.Id, OutlookEntryId = created.OutlookEntryId, StarfaceId = sc.StarfaceId, LastSyncHash = sc.GetHash() }); result.Created++; } } } } catch (Exception ex) { result.Errors++; result.ErrorMessages.Add($"{sc.DisplayName}: {ex.Message}"); } } } _profileManager.UpdateLastSync(profile.Id); _profileManager.SaveMappings(profile.Id, mappings); await starface.LogoutAsync(); Log("Synchronisation abgeschlossen!"); } } catch (Exception ex) { result.Errors++; result.ErrorMessages.Add($"Allgemeiner Fehler: {ex.Message}"); } return result; } } }