public WorkitemStateChangeCollection GetWorkitemsClosedSince(DateTime closedSince, string baseWorkitemType, string sourceName, string lastCheckedDefectId) {
            var results = new WorkitemStateChangeCollection();
            var lastChangedIdLocal = lastCheckedDefectId;
            var dateLastChange = closedSince;

            try {
                var workitems = GetClosedWorkitems(closedSince, baseWorkitemType, sourceName);

                foreach(var item in workitems) {
                    var id = item.Number;
                    var changeDateUtc = item.ChangeDateUtc;

                    logger.Log(LogMessage.SeverityType.Debug, string.Format("Processing V1 Defect {0} closed at {1}", id, changeDateUtc));

                    if(lastCheckedDefectId.Equals(id)) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tSkipped because this ID was processed last time");
                        continue;
                    }

                    if (WorkitemHasOpenDuplicate(item)) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tSkipped because the workitem has opened duplicate.");
                        continue;                        
                    }

                    if((dateLastChange == DateTime.MinValue && changeDateUtc != DateTime.MinValue) || changeDateUtc.CompareTo(dateLastChange) > 0) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tCaused an update to LastChangeID and dateLastChanged");
                        dateLastChange = changeDateUtc;
                        lastChangedIdLocal = id;
                    }

                    results.Add(new WorkitemStateChangeResult(item.GetProperty<string>(configuration.ExternalIdFieldName), item.Number));
                }
            } catch(WebException ex) {
                ShowError(ex);
            }

            results.LastCheckedDefectId = lastChangedIdLocal;
            results.QueryTimeStamp = dateLastChange;

            return results;
        }
        private void SynchronizeItemsToJira(WorkitemStateChangeCollection readyForSynchronisationToJira)
        {
            if (readyForSynchronisationToJira.Count <= 0) {
                logger.Log(LogMessage.SeverityType.Info, string.Format("Skipping Jira update - nothing to synchronize", readyForSynchronisationToJira.Count));
                return;
            }

            logger.Log(LogMessage.SeverityType.Info, string.Format("There are {0} items to update in Jira.", readyForSynchronisationToJira.Count));
            eventManager.Publish(readyForSynchronisationToJira);

            foreach (var result in readyForSynchronisationToJira.Where(result => result.ChangesProcessed)) {
                logger.Log(LogMessage.SeverityType.Info, string.Format("Issue {0} updated successfully in Jira.", result.ExternalId));
            }
        }
        private void SynchronizeItems(WorkitemsToUpdate itemsModifiedInJira, WorkitemStateChangeCollection itemsModifiedInVersionOne)
        {
            logger.Log(string.Format("There are {0} items modified in Jira", itemsModifiedInJira.workitemsForUpdate.Count));
            logger.Log(string.Format("There are {0} items modified in VersionOne", itemsModifiedInVersionOne.Count));

            WorkitemStateChangeCollection readyForSynchronisationToJira = new WorkitemStateChangeCollection();
            readyForSynchronisationToJira.copyFrom(itemsModifiedInVersionOne);

            List<Workitem> readyForSynchronizationToVersionOne = new List<Workitem>();

            foreach (Workitem jiraIssue in itemsModifiedInJira.workitemsForUpdate) {
                WorkitemStateChangeResult v1item = itemsModifiedInVersionOne.Find(x => x.ExternalId.Equals(jiraIssue.ExternalId)  );
                if (v1item == null) {
                    logger.Log(string.Format("Issue {0} modifie in JIRA ONLY and not modified in VersionOne", jiraIssue.ExternalId));
                    readyForSynchronizationToVersionOne.Add(jiraIssue);
                    continue;
                }

                if (jiraIssue.Updated.Value.CompareTo(v1item.ChangeDateUtc) > 0) {
                    logger.Log(string.Format("Issue {0} modified in JIRA on {1} and in VersionOne on {2} - JIRA update is newer", jiraIssue.ExternalId, jiraIssue.Updated.Value, v1item.ChangeDateUtc));
                    readyForSynchronizationToVersionOne.Add(jiraIssue);
                } else {
                    logger.Log(string.Format("Issue {0} modified in JIRA on {1} and in VersionOne on {2} - VersionOne update is newer", jiraIssue.ExternalId, jiraIssue.Updated, v1item.ChangeDateUtc));
                    readyForSynchronisationToJira.Add(v1item);
                }
            }

            foreach (var v1item in itemsModifiedInVersionOne) {
                if (itemsModifiedInJira.workitemsForUpdate.Exists(x => x.ExternalId.Equals(v1item.ExternalId)) == false) {
                    logger.Log(string.Format("Issue {0} modified in VersionOne ONLY and not modified in JIRA", v1item.ExternalId));
                    readyForSynchronisationToJira.Add(v1item);
                }
                else {
                    logger.Log(string.Format("Issue {0} modified in VersionOne and JIRA", v1item.ExternalId));
                    //poprzednia pętla mi załatwia ten warunek - nic nie trzeba juz tu dodawać
                }
            }

            SynchronizeItemsToJira(readyForSynchronisationToJira);
            SynchronizeItemsToVersionOne(readyForSynchronizationToVersionOne);
        }
        private void createJiraUpdateResult(WorkitemFromExternalSystem item, WorkitemStateChangeCollection results)
        {
            var jiraUpdate = new WorkitemStateChangeResult(item.ExternalId, item.Number, item.ChangeDateUtc);

            var StatusName = String.IsNullOrEmpty(item.StatusName) ? String.Empty : item.StatusName;
            jiraUpdate.FieldUpdates.Add(Entity.StatusNameProperty, StatusName);

            results.Add(jiraUpdate);
        }
        public WorkitemStateChangeCollection GetWorkitemsReadyForSynchronisation(DateTime modifiedSince, string sourceName, string externalIdFieldName, string lastModifiedItemId)
        {
            var results = new WorkitemStateChangeCollection();
            var lastChangeDateLocal = modifiedSince;
            var lastLocalModifiedItemId = lastModifiedItemId;

            try {
                var filter = FilterItemsModifiedSince(modifiedSince, sourceName);
                var workitems = v1Processor.GetPrimaryWorkitems(filter).Select(x => new WorkitemFromExternalSystem(x, externalIdFieldName));

                foreach (var item in workitems) {
                    var id = item.Number;
                    var changeDateUtc = item.ChangeDateUtc;

                    logger.Log(LogMessage.SeverityType.Debug, string.Format("Processing V1 Workitem {0} modified at {1}", id, changeDateUtc));

                    if (lastLocalModifiedItemId.Equals(id) == true && modifiedSince.CompareTo(changeDateUtc) == 0) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tSkipped because this ID was processed last time");
                        continue;
                    }

                    if (lastChangeDateLocal.CompareTo(changeDateUtc) < 0) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tCaused an update to lastChangeDateLocal and QueryTimeStamp");
                        lastChangeDateLocal = changeDateUtc;
                        lastLocalModifiedItemId = id;
                    }

                    createJiraUpdateResult(item, results);

                }
            } catch (WebException ex) {
                string responseMessage = null;

                if (ex.Response != null) {
                    using (var reader = new StreamReader(ex.Response.GetResponseStream())) {
                        responseMessage = reader.ReadToEnd();
                    }
                }

                logger.Log(LogMessage.SeverityType.Error, string.Format("Error querying VersionOne ({0}) for modified external defects:\r\n{1}\r\n\r\n{2}", ex.Response.ResponseUri, responseMessage, ex));
            } catch (Exception ex) {
                logger.Log(LogMessage.SeverityType.Error, string.Format("Error while creating update result, reason: {0}", ex.Message));
            }

            results.QueryTimeStamp = lastChangeDateLocal;
            results.LastCheckedDefectId = lastLocalModifiedItemId;

            return results;
        }
        public WorkitemStateChangeCollection GetWorkitemsClosedSince(DateTime closedSince, string sourceName, string externalIdFieldName, string lastCheckedDefectId)
        {
            var results = new WorkitemStateChangeCollection();
            var lastCheckedDefectIdLocal = lastCheckedDefectId;
            var lastChangeDateLocal = closedSince;

            try {
                var filters = new List<IFilter> {
                    Filter.Closed(true),
                    Filter.OfTypes(VersionOneProcessor.StoryType, VersionOneProcessor.DefectType),
                    Filter.Equal(Entity.SourceNameProperty, sourceName),
                };

                if (closedSince != DateTime.MinValue) {
                    filters.Add(Filter.Greater(Entity.ChangeDateUtcProperty, closedSince));
                }

                var filter = GroupFilter.And(filters.ToArray());

                var workitems = v1Processor.GetPrimaryWorkitems(filter).Select(x => new WorkitemFromExternalSystem(x, externalIdFieldName));

                foreach(var item in workitems) {
                    var id = item.Number;
                    var changeDateUtc = item.ChangeDateUtc;

                    logger.Log(LogMessage.SeverityType.Debug, string.Format("Processing V1 Defect {0} closed at {1}", id, changeDateUtc));

                    if(lastCheckedDefectId.Equals(id)) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tSkipped because this ID was processed last time");
                        continue;
                    }

                    if(closedSince.CompareTo(changeDateUtc) >= 0) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tSkipped because the ChangeDate is less than the date/time we last checked for changes");
                        continue;
                    }

                    if((lastChangeDateLocal == DateTime.MinValue && changeDateUtc != DateTime.MinValue) || changeDateUtc.CompareTo(lastChangeDateLocal) > 0) {
                        logger.Log(LogMessage.SeverityType.Debug, "\tCaused an update to LastChangeID and dateLastChanged");
                        lastChangeDateLocal = changeDateUtc;
                        lastCheckedDefectIdLocal = id;
                    }

                    var result = new WorkitemStateChangeResult(item.ExternalId, item.Number, item.ChangeDateUtc);
                    result.FieldUpdates.Add(Entity.StatusNameProperty, JiraClosedStatus );
                    results.Add(result);
                }
            } catch(WebException ex) {
                string responseMessage = null;

                if(ex.Response != null) {
                    using(var reader = new StreamReader(ex.Response.GetResponseStream())) {
                        responseMessage = reader.ReadToEnd();
                    }
                }

                logger.Log(LogMessage.SeverityType.Error, string.Format("Error querying VersionOne ({0}) for closed external defects:\r\n{1}\r\n\r\n{2}", ex.Response.ResponseUri, responseMessage, ex));
            }

            results.LastCheckedDefectId = lastCheckedDefectIdLocal;
            results.QueryTimeStamp = lastChangeDateLocal;

            return results;
        }