using Blueberry.Redmine.Dto; using Microsoft.Extensions.Logging; namespace Blueberry.Redmine { public class RedmineManager { private readonly TimeSpan DEFAULT_CACHE_DURATION = TimeSpan.FromHours(1); private readonly RedmineConfig _config; private readonly ILogger _logger; private readonly RedmineApiClient _apiClient; private readonly RedmineCache _statusCache; private readonly RedmineCache _priorityCache; private readonly RedmineCache _customFieldCache; private readonly RedmineCache _projectCache; public RedmineManager(RedmineConfig config, HttpClient client, ILoggerFactory loggerFactory) { _config = config; _apiClient = new RedmineApiClient(config, loggerFactory.CreateLogger(), client); _statusCache = new RedmineCache( DEFAULT_CACHE_DURATION, loggerFactory.CreateLogger>(), cacheFilePath: $"{_config.CacheFilePath}Statuses.json"); _priorityCache = new RedmineCache( DEFAULT_CACHE_DURATION, loggerFactory.CreateLogger>(), cacheFilePath: $"{_config.CacheFilePath}Priorities.json"); _customFieldCache = new RedmineCache( DEFAULT_CACHE_DURATION, loggerFactory.CreateLogger>(), cacheFilePath: $"{_config.CacheFilePath}CustomFields.json"); _projectCache = new RedmineCache( DEFAULT_CACHE_DURATION, loggerFactory.CreateLogger>(), cacheFilePath: $"{_config.CacheFilePath}Projects.json"); _logger = loggerFactory.CreateLogger(); _logger.LogDebug("Initialized caches"); } public async Task IsRedmineAvailable(CancellationToken? token = null) { try { await _apiClient.GetUserAsync(token: token); return true; } catch (Exception) { return false; } } public async Task> GetStatusesAsync(CancellationToken? token = null) { if (_statusCache.IsCacheValid()) { return await _statusCache.GetItemsAsync(); } var statuses = await _apiClient.GetStatusesAsync(token); await _statusCache.RefreshCacheAsync(statuses); return statuses; } public async Task> GetPrioritiesAsync(CancellationToken? token = null) { if (_priorityCache.IsCacheValid()) { return await _priorityCache.GetItemsAsync(); } var priorities = await _apiClient.GetPrioritiesAsync(token); await _priorityCache.RefreshCacheAsync(priorities); return priorities; } public async Task> GetCustomFieldsAsync(CancellationToken? token = null) { if (_customFieldCache.IsCacheValid()) { return await _customFieldCache.GetItemsAsync(); } var fields = await _apiClient.GetCustomFieldsAsync(token); await _customFieldCache.RefreshCacheAsync(fields); return fields; } public async Task> GetProjectsAsync(int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { if (_projectCache.IsCacheValid()) { return await _projectCache.GetItemsAsync(); } var projects = await _apiClient.GetProjects(limit, progress, token); await _projectCache.RefreshCacheAsync(projects); return projects; } public async Task GetCurrentUserAsync(CancellationToken? token = null) { var user = await _apiClient.GetUserAsync(token: token); return user; } public async Task GetUserAsync(int userId, CancellationToken? token = null) { var user = await _apiClient.GetUserAsync(userId, token: token); return user; } public async Task> GetCurrentUserIssuesAsync(int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { var user = await GetCurrentUserAsync(token); return await _apiClient.GetOpenIssuesByAssignee(user.Id, limit, progress, token); } public async Task GetCurrentUserTimeAsync(DateTime start, DateTime end, int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { var user = await GetCurrentUserAsync(token); return await _apiClient.GetTotalTimeForUser(user.Id, start, end, limit, progress, token); } public async Task GetIssueAsync(int issueId, CancellationToken? token = null) { return await _apiClient.GetIssue(issueId, token); } public async Task GetSimpleIssueAsync(int issueId, CancellationToken? token = null) { return await _apiClient.GetSimpleIssue(issueId, token); } public async Task> GetProjectTrackersAsync(int projectId, CancellationToken? token = null) { return await _apiClient.GetTrackersForProject(projectId.ToString(), token); } public async Task> GetTimeOnIssue(int issueId, int limit = 25, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { var result = await _apiClient.GetTimeOnIssue(issueId, limit, progress, token); return result; } public async Task SetIssueStatusAsync(int issueId, int statusId, CancellationToken? token = null) { await _apiClient.SetIssueStatus(issueId, statusId, token); } public async Task LogTimeAsync(int issueId, double hours, string comments, DateTime? date = null, int? activityId = null, CancellationToken? token = null) { await _apiClient.LogTimeAsync(issueId, hours, comments, date, activityId, token); } public async Task CreateIssueAsync(int projectId, int trackerId, string subject, string description, double estimatedHours, int priorityId, int? assigneeId = null, int? parentIssueId = null, CancellationToken? token = null) { return await _apiClient.CreateNewIssue(projectId, trackerId, subject, description, estimatedHours, priorityId, assigneeId, parentIssueId, token); } public async Task GetCurrentUserTimeTodayAsync(int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { return await GetCurrentUserTimeAsync(DateTime.Today, DateTime.Today, limit, progress, token); } public async Task GetCurrentUserTimeYesterdayAsync(int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { return await GetCurrentUserTimeAsync(DateTime.Today.AddDays(-1), DateTime.Today.AddDays(-1), limit, progress, token); } public async Task GetCurrentUserTimeThisMonthAsync(int limit = 50, IProgress<(int, int)>? progress = null, CancellationToken? token = null) { var start = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); var end = start.AddMonths(1).AddDays(-1); return await GetCurrentUserTimeAsync(start, end, limit, progress, token); } } }