using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Options; using MimeKit; using vendorApproverBackend.Entities; using vendorApproverBackend.Interfaces; using vendorApproverBackend.Settings; namespace vendorApproverBackend.Services; public class EmailService : IEmailService { private readonly ILogger _logger; private readonly SmtpSettings _smtpSettings; public EmailService(ILogger logger, IOptions smtpSettings) { _logger = logger; _smtpSettings = smtpSettings.Value; } public async Task SendOtpEmailAsync(string to, User user, string otpCode) { try { string subject = "Your OETC Verification Code"; string emailContent = GenerateOtpEmailTemplate(user, otpCode); await SendEmailAsync(subject, to, emailContent); _logger.LogInformation("OTP email sent successfully to {Email}", to); } catch (Exception ex) { _logger.LogError(ex, "Error sending OTP email to {Email}", to); throw; } } public async Task SendEmployeeEmailAsync(string to, User user, string password) { try { string subject = "Your OETC Account Credentials"; string emailContent = GenerateEmployeeEmailTemplate(user, password); await SendEmailAsync(subject, to, emailContent); _logger.LogInformation("OTP email sent successfully to {Email}", to); } catch (Exception ex) { _logger.LogError(ex, "Error sending OTP email to {Email}", to); throw; } } public async Task SendSubAdminEmailAsync(string to, User user, string password) { try { string subject = "Your OETC Account Credentials"; string emailContent = GenerateSubAdminEmailTemplate(user, password); await SendEmailAsync(subject, to, emailContent); _logger.LogInformation("OTP email sent successfully to {Email}", to); } catch (Exception ex) { _logger.LogError(ex, "Error sending OTP email to {Email}", to); throw; } } public async Task SendOtpForgetPasswordAsync(string to, User user, string otpCode) { try { string subject = "Your OETC Verification Code"; string emailContent = GenerateOtpForgetPassword(user, otpCode); await SendEmailAsync(subject, to, emailContent); _logger.LogInformation("OTP email sent successfully to {Email}", to); } catch (Exception ex) { _logger.LogError(ex, "Error sending OTP email to {Email}", to); throw; } } public async Task SendSubAdminWelcomeEmailAsync(string to, User user, string password) { try { string subject = "Welcome to Product Approval System"; string emailContent = GenerateWelcomeEmailTemplate(user, password); await SendEmailAsync(subject, to, emailContent); _logger.LogInformation("Welcome email sent successfully to {Email}", to); } catch (Exception ex) { _logger.LogError(ex, "Error sending welcome email to {Email}", to); throw; } } private async Task SendEmailAsync(string subject, string to, string htmlContent) { try { var mailMessage = new MimeMessage(); mailMessage.From.Add(new MailboxAddress(_smtpSettings.FromName, _smtpSettings.FromEmail)); mailMessage.To.Add(new MailboxAddress("", to)); mailMessage.Subject = subject; var bodyBuilder = new BodyBuilder { HtmlBody = htmlContent }; mailMessage.Body = bodyBuilder.ToMessageBody(); using (var smtp = new SmtpClient()) { await smtp.ConnectAsync(_smtpSettings.Host, _smtpSettings.Port, SecureSocketOptions.StartTls); await smtp.AuthenticateAsync(_smtpSettings.Username, _smtpSettings.Password); await smtp.SendAsync(mailMessage); await smtp.DisconnectAsync(true); } } catch (Exception ex) { _logger.LogError(ex, "Failed to send email to {Email}", to); throw; } } private string GenerateOtpEmailTemplate(User user, string otpCode) { return $@"

Hello {user.Name}!

You have requested a verification code for your OETC account.

Please use the following One-Time Password (OTP) to complete your verification:

{otpCode}

This code will expire in 2 minutes for security purposes.

If you did not request this code, please ignore this email or contact support if you have concerns.

Go to Dashboard
"; } private string GenerateOtpForgetPassword(User user, string otpCode) { return $@"

Hello {user.Name}!

You have requested a verification code to update the password for your OETC account.

Please use the following One-Time Password (OTP) to proceed with the password update.

{otpCode}

This code will expire in 10 minutes for security purposes.

If you did not request this code, please ignore this email or contact support if you have concerns.

Go to Dashboard
"; } private string GenerateSubAdminEmailTemplate(User user, string password) { return $@"

Welcome to OETC!

Your account has been successfully created by the admin,

granting you access to the OETC Admin dashboard.

You have successfully been registered to use OETC Dashboard as a Sub Admin.

Your login credentials are provided below:

Email: {user.Email}
Password: {password}
Redirect to Dashboard

The password was auto-generated; however, feel free to change it.

"; } private string GenerateEmployeeEmailTemplate(User user, string password) { return $@"

Welcome to OETC!

Your account has been successfully created by the admin,

granting you access to the OETC Admin dashboard.

You have successfully been registered to use OETC Dashboard

Your login credentials are provided below:

Email: {user.Email}
Password: {password}
Redirect to Dashboard

The password was auto-generated; however, feel free to change it.

"; } private string GenerateWelcomeEmailTemplate(User user, string password) { return $@"

Hello {user.Name},

Welcome to the Product Approval System!

Your login password is:

{password}

"; } // Method for the Vendor public async Task SendFactoryVisitRequestedVendorAsync(string to, User user) { string subject = "Action Required: Factory Visit Plan"; string emailContent = $@"

Congratulations {user.Name}!

You have passed the first stage of the prequalification process.

Please log in to the dashboard to upload your Factory Visit Plan to proceed.

Upload Visit Plan
"; await SendEmailAsync(subject, to, emailContent); } // request factory visit mails public async Task SendFactoryVisitAdminAlertAsync(string to, string vendorName, VisitRequest request) { string title = "New Factory Visit Requested"; string content = $@"

The vendor {vendorName} has proposed a new factory visit.

Location:{request.Location}
Start Date:{request.StartDate:yyyy-MM-dd}
End Date:{request.EndDate:yyyy-MM-dd}
Purpose:{request.VisitPurpose}
"; // Using your professional layout for a consistent look string html = GetProfessionalLayout(title, content, "View Request Details", "https://productapproval.rooted.om"); await SendEmailAsync($"[Admin Alert] New Visit Request: {vendorName}", to, html); } public async Task SendFactoryVisitVendorActionAsync(string to, User user) { string title = $"Next Steps: Factory Visit Plan"; string content = $@"

Dear {user.Name},

We are pleased to inform you that your prequalification request has successfully cleared the initial review phase.

To proceed with the Factory Visit stage, you are now required to submit a comprehensive visit plan through the portal. This document is mandatory for our technical team to schedule the inspection.

"; string html = GetProfessionalLayout(title, content, "Upload Visit Plan", "https://productapproval.rooted.om"); await SendEmailAsync("Action Required: Factory Visit Plan Submission", to, html); } private string GetProfessionalLayout(string title, string content, string buttonText, string buttonUrl) { return $@"

OETC PORTAL

{title}

{content}

© {DateTime.Now.Year} Oman Electricity Transmission Company (OETC). All rights reserved.

This is an automated message, please do not reply directly to this email.

"; } public async Task SendVisitRequestAcceptedAsync(string to, User user, VisitRequest request) { string title = "Factory Visit Request Approved"; string content = $@"

Dear {user.Name},

We are pleased to inform you that your proposed factory visit schedule has been Approved.

Location:{request.Location}
Scheduled Date:{request.StartDate:dd MMM yyyy}
Contact Person:{request.ContactPerson}

Our technical team will proceed with the inspection as planned. Please ensure all necessary safety protocols and documentation are ready at the site.

"; string html = GetProfessionalLayout(title, content, "View Details", "https://productapproval.rooted.om"); await SendEmailAsync("Update: Factory Visit Request Approved", to, html); } public async Task SendVisitRequestRejectedAsync(string to, User user, string reason) { string title = "Factory Visit Request - Action Required"; string content = $@"

Dear {user.Name},

Your recent factory visit proposal has been Rejected by the review committee.

Reason for Rejection:

{reason ?? "No specific reason provided. Please contact the administrator."}

Please review the comments above and submit a revised visit plan through the portal to proceed with your application.

"; string html = GetProfessionalLayout(title, content, "Submit New Proposal", "https://productapproval.rooted.om"); await SendEmailAsync("Action Required: Factory Visit Request Rejected", to, html); } // request letter mailing public async Task SendVendorLetterSubmittedAsync(string to, User user, VendorRequestLetter letter) { string title = "Request Letter Submitted Successfully"; string content = $@"

Dear {user.Name},

Your Vendor Request Letter has been successfully submitted to the OETC portal on {letter.SubmissionDate:dd MMM yyyy}.

Our administration team will now review your details and the associated product list. You can track the real-time status of your application through your dashboard.

"; string html = GetProfessionalLayout(title, content, "View Application Status", "https://productapproval.rooted.om"); await SendEmailAsync("Confirmation: Vendor Request Letter Received", to, html); } public async Task SendAdminNewLetterAlertAsync(string to, string vendorName, VendorRequestLetter letter) { string title = "New Vendor Request Letter Received"; // Generate Product Rows string productRows = ""; if (letter.LetterProducts != null && letter.LetterProducts.Any()) { foreach (var p in letter.LetterProducts) { productRows += $@" {p.Category} {p.ProductName} {p.ProductType} "; } } string content = $@"

A new request letter has been received from: {vendorName}.

Submission Date:{letter.SubmissionDate:dd MMM yyyy}
Establishment Date:{letter.DateEstablished:yyyy-MM-dd}

Proposed Products List:

{productRows}
Category Product Name Type
"; string html = GetProfessionalLayout(title, content, "Review Application", "https://productapproval.rooted.om"); await SendEmailAsync($"[Admin Alert] New Request Letter: {vendorName}", to, html); } // prequalification request : public async Task SendAuditAssignmentNotificationAsync(string to, User auditor, RequestRenew request) { // Logic: Calculate Deadline (Assignment Date + 30 days) DateTime assignmentDate = request.AssignDate ?? DateTime.UtcNow; DateTime deadline = assignmentDate.AddDays(30); string title = "New Audit Assignment: Prequalification Renewal"; string content = $@"

Dear {auditor.Name},

You have been officially assigned as the Performance Evaluator for a new prequalification renewal request.

Request ID: #{request.Id}
Assignment Date: {assignmentDate:dd MMM yyyy}
Submission Deadline: {deadline:dd MMM yyyy}

Please log in to the portal to review the vendor documentation and submit your performance evaluation within the 30-day window.

"; string html = GetProfessionalLayout(title, content, "Access Audit Dashboard", "https://productapproval.rooted.om"); await SendEmailAsync($"[Action Required] New Audit Assignment: Request #{request.Id}", to, html); } public async Task SendAuditDeadlineReminderAsync(string to, User auditor, PrequalificationRequest request, int daysRemaining) { DateTime assignmentDate = request.AssignmentDate ?? DateTime.UtcNow; DateTime deadline = assignmentDate.AddDays(30); string title = "Urgent: Audit Deadline Approaching"; string content = $@"

Dear {auditor.Name},

This is an automated reminder regarding the Prequalification Request #{request.Id} assigned to you.

⚠️ Only {daysRemaining} days remaining until the deadline.

Assignment Date: {assignmentDate:dd MMM yyyy}
Final Deadline: {deadline:dd MMM yyyy}

Please ensure your evaluation is submitted through the OETC portal before the cutoff date to avoid application delays.

"; string html = GetProfessionalLayout(title, content, "Complete Evaluation", "https://productapproval.rooted.om/admin/evaluations"); await SendEmailAsync($"[Reminder] {daysRemaining} Days Left: Audit Request #{request.Id}", to, html); } // manager assignment + product decision mails (vendor request letter) public async Task SendLetterAssignedToManagerAsync(string to, User manager, VendorRequestLetter letter) { string title = "Request Letter Assigned for Review"; string content = $@"

Dear {manager.Name},

Vendor Request Letter #{letter.Id} ({letter.FullName ?? letter.ShortName ?? "A Vendor"}) has been assigned to you for product review.

Please log in to the portal to review the proposed products and submit your decision.

"; string html = GetProfessionalLayout(title, content, "Review Request Letter", "https://productapproval.rooted.om"); await SendEmailAsync($"[Action Required] Request Letter #{letter.Id} Assigned to You", to, html); } public async Task SendAdminProductManagerDecisionAsync(string to, LetterProducts product, VendorRequestLetter letter, bool approved) { string statusLabel = approved ? "Approved" : "Rejected"; string title = $"Product {statusLabel} by Manager"; string reasonHtml = !approved && !string.IsNullOrEmpty(product.RejectionReason) ? $@"

Rejection Reason:

{product.RejectionReason}

" : ""; string content = $@"

The Manager has {statusLabel} the following product on Request Letter #{letter.Id}:

Product Name:{product.ProductName}
Category:{product.Category}
{reasonHtml}

Please log in to the portal to make the final decision.

"; string html = GetProfessionalLayout(title, content, "Review Product", "https://productapproval.rooted.om"); await SendEmailAsync($"[Manager Decision] {statusLabel}: {product.ProductName}", to, html); } public async Task SendVendorProductAdminDecisionAsync(string to, User vendorUser, LetterProducts product, VendorRequestLetter letter, bool approved) { string statusLabel = approved ? "Approved" : "Rejected"; string title = $"Product {statusLabel}"; string reasonHtml = !approved && !string.IsNullOrEmpty(product.RejectionReason) ? $@"

Rejection Reason:

{product.RejectionReason}

" : ""; string content = $@"

Dear {vendorUser.Name},

Your product submitted under Request Letter #{letter.Id} has been {statusLabel} by the admin.

Product Name:{product.ProductName}
Category:{product.Category}
{reasonHtml}"; string html = GetProfessionalLayout(title, content, "View Application Status", "https://productapproval.rooted.om"); await SendEmailAsync($"Update: Product {statusLabel} - Request Letter #{letter.Id}", to, html); } public async Task SendManualPqReminderAsync(string to, PrequalificationRequest request, string? adminComment = null) { string title = "Prequalification Request Follow-up"; // Logic for the optional comment block string commentHtml = !string.IsNullOrEmpty(adminComment) ? $@"

Admin Note:

{adminComment}

" : ""; string content = $@"

This is a formal follow-up regarding the Prequalification Request #{request.Id}.

{commentHtml}

Request Details:

  • Status: {request.Status}
  • Submission Date: {request.SubmissionDate:dd MMM yyyy}

Please review the request and provide the necessary updates or information via the portal.

"; string html = GetProfessionalLayout(title, content, "View Request", $"https://productapproval.rooted.om/admin/pq-requests/{request.Id}"); await SendEmailAsync($"[Action Required] Follow-up: PQ Request #{request.Id}", to, html); } }