using CvMatcher.Models.Responses; using CvSearch.Data.Entities; using EmailApi.Models.Clients; using EmailApi.Models.Requests; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using MyAi.Data.Services; namespace CvSearchJob.Services; public sealed class CvSearchEmailSender { private readonly IEmailApiClient _emailApi; private readonly ITemplateService _templates; private readonly IConfiguration _config; private readonly ILogger _logger; public CvSearchEmailSender( IEmailApiClient emailApi, ITemplateService templates, IConfiguration config, ILogger logger) { _emailApi = emailApi; _templates = templates; _config = config; _logger = logger; } public async Task SendResultsAsync( string toEmail, string? attachmentFileName, IReadOnlyList results, string language, CancellationToken ct) { var contactToEmail = _config["Contact:ToEmail"]; var recipients = new List(); if (!string.IsNullOrWhiteSpace(toEmail)) recipients.Add(toEmail); if (!string.IsNullOrWhiteSpace(contactToEmail) && !recipients.Any(r => string.Equals(r, contactToEmail, StringComparison.OrdinalIgnoreCase))) recipients.Add(contactToEmail); if (recipients.Count == 0) return; var htmlBody = BuildBody(results, language); var subject = _templates.Render("email.search-results.subject", language, ("count", results.Count.ToString())); try { await _emailApi.SendAsync(new SendEmailRequest { To = recipients, Subject = subject, HtmlBody = htmlBody, AttachmentPath = attachmentFileName }, ct); _logger.LogInformation("Job search results email sent to {Recipients}", string.Join(", ", recipients)); } catch (Exception ex) { _logger.LogError(ex, "Failed to send job search results email to {Recipients}", string.Join(", ", recipients)); } } private string BuildBody(IReadOnlyList results, string language) { if (results.Count == 0) return _templates.Get("email.search-results.empty", language); var items = new System.Text.StringBuilder(); for (int i = 0; i < results.Count; i++) { var r = results[i]; var matchResp = TryParseResult(r.ResultJson); var summary = matchResp?.Summary; items.Append($"""
{i + 1}. {r.JobTitle} {r.Score}% match [{r.ProviderName}]
{r.JobUrl} {(string.IsNullOrWhiteSpace(summary) ? "" : $"

{summary}

")}
"""); } return _templates.Render("email.search-results.body", language, ("count", results.Count.ToString()), ("items", items.ToString())); } private static JobMatchResponse? TryParseResult(string json) { try { return System.Text.Json.JsonSerializer.Deserialize(json, new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web)); } catch { return null; } } }