using Api.Services.Contracts;
using Microsoft.Extensions.Options;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
using Models.Settings;
using Models.Requests;
using CvMatcher.Models.Responses;

namespace Api.Services
{
    public sealed class SmtpEmailSender : IEmailSender
    {
        private readonly SmtpSettings _smtp;
        private readonly ContactSettings _contact;
        private readonly SubscribeSettings _subscribe;
        private readonly FileStorageSettings _fileStorage;
        private readonly ILogger<SmtpEmailSender> _log;
        private readonly string _environmentName;

        public SmtpEmailSender(IOptions<SmtpSettings> smtp,
            IOptions<ContactSettings> contact,
            IOptions<SubscribeSettings> subscribe,
            IOptions<FileStorageSettings> fileStorage,
            ILogger<SmtpEmailSender> log)
        {
            _smtp = smtp.Value;
            _contact = contact.Value;
            _subscribe = subscribe.Value;
            _fileStorage = fileStorage.Value;
            _log = log;
            // Use APP_ENVIRONMENT_NAME from environment variable (set in docker-compose) with fallback to "Development"
            _environmentName = Environment.GetEnvironmentVariable("APP_ENVIRONMENT_NAME") ?? "Development";
        }

        public async Task SendContactAsync(ContactRequest req, CancellationToken ct)
        {
            // Throw error if ToEmail is not configured, since contact requests are important to process.
            if (string.IsNullOrWhiteSpace(_contact.ToEmail))
            {
                _log.LogDebug("Contact email skipped - ToEmail not configured");
                throw new InvalidOperationException("Contact email recipient is not configured.");
            }

            _log.LogInformation("Preparing contact email from {SenderEmail} to {RecipientEmail}",
                req.Email, _contact.ToEmail);

            var msg = new MimeMessage();
            msg.From.Add(MailboxAddress.Parse(_smtp.Username));
            msg.To.Add(MailboxAddress.Parse(_contact.ToEmail));
            msg.ReplyTo.Add(MailboxAddress.Parse(req.Email));
            msg.Subject = $"{_contact.SubjectPrefix} [{_environmentName}] {req.Subject}".Trim();

            var body =
                $@"New contact form submission:

            Name: {req.Name}
            Email: {req.Email}
            Subject: {req.Subject}

            Message:
            {req.Message}
            ";

            msg.Body = new TextPart("plain") { Text = body };

            await SendEmailAsync(msg, "contact email", ct);

            _log.LogInformation("Contact email sent successfully from {SenderEmail}", req.Email);
        }

        public async Task SendSubscribeAsync(SubscribeRequest req, CancellationToken ct)
        {
            // Throw error if ToEmail is not configured, since subscription requests are important to process.
            if (string.IsNullOrWhiteSpace(_subscribe.ToEmail))
            {
                _log.LogDebug("Subscription email skipped - ToEmail not configured");
                throw new InvalidOperationException("Subscription email recipient is not configured.");
            }

            _log.LogInformation("Processing subscription request for {Email}", req.Email);

            var msg = new MimeMessage();
            msg.From.Add(MailboxAddress.Parse(_smtp.Username));
            msg.To.Add(MailboxAddress.Parse(_subscribe.ToEmail));
            msg.ReplyTo.Add(MailboxAddress.Parse(req.Email));
            msg.Subject = $"{_subscribe.SubjectPrefix} [{_environmentName}]".Trim();

            var body =
                $@"New subscription request:

            Email: {req.Email}
            ";

            msg.Body = new TextPart("plain") { Text = body };

            await SendEmailAsync(msg, "subscription email", ct);

            _log.LogInformation("Subscription email sent successfully for {Email}", req.Email);
        }

        public async Task SendFileDownloadNotificationAsync(string fileName, string? userIp, CancellationToken ct)
        {
            // Skip sending if ToEmail is not configured
            if (string.IsNullOrWhiteSpace(_fileStorage.ToEmail))
            {
                _log.LogDebug("File download notification skipped - ToEmail not configured");
                return;
            }

            _log.LogInformation("Preparing file download notification for {FileName}", fileName);

            var msg = new MimeMessage();
            msg.From.Add(MailboxAddress.Parse(_smtp.Username));
            msg.To.Add(MailboxAddress.Parse(_fileStorage.ToEmail));
            msg.Subject = $"{_fileStorage.SubjectPrefix} [{_environmentName}] {fileName}".Trim();

            var body =
                $@"File download notification:

            File: {fileName}
            Downloaded at: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC
            IP Address: {userIp ?? "Unknown"}
            ";

            msg.Body = new TextPart("plain") { Text = body };

            await SendEmailAsync(msg, "file download notification email", ct);

            _log.LogInformation("File download notification sent successfully for {FileName}", fileName);
        }

        /// <summary>
        /// Connects to the SMTP server and authenticates if credentials are configured.
        /// </summary>
        private async Task ConnectAndAuthenticateAsync(SmtpClient client, CancellationToken ct)
        {
            // If you're in enterprise environments, you may need to tweak certificate validation.
            // Don't disable it casually.
            var tls = _smtp.UseStartTls ? SecureSocketOptions.StartTls : SecureSocketOptions.Auto;

            _log.LogDebug("Connecting to SMTP server {Host}:{Port} with security={Security}",
                _smtp.Host, _smtp.Port, tls);

            await client.ConnectAsync(_smtp.Host, _smtp.Port, tls, ct);

            if (!string.IsNullOrWhiteSpace(_smtp.Username))
            {
                _log.LogDebug("Authenticating with SMTP server as {Username}", _smtp.Username);
                await client.AuthenticateAsync(_smtp.Username, _smtp.Password, ct);
            }
        }

        /// <summary>
        /// Sends an email message using SMTP.
        /// </summary>
        /// <param name="message">The email message to send.</param>
        /// <param name="messageType">Description of the message type for logging purposes.</param>
        /// <param name="ct">Cancellation token.</param>
        private async Task SendEmailAsync(MimeMessage message, string messageType, CancellationToken ct)
        {
            using var client = new SmtpClient();

            await ConnectAndAuthenticateAsync(client, ct);

            _log.LogDebug("Sending {MessageType} message", messageType);
            await client.SendAsync(message, ct);
            await client.DisconnectAsync(true, ct);
        }

        public async Task SendMatchAsync(string? explicitTo, string subject, string body, string? attachmentPath, CancellationToken ct)
        {
            var recipients = new List<string>();
            if (!string.IsNullOrWhiteSpace(explicitTo))
            {
                recipients.Add(explicitTo);
            }

            if (!string.IsNullOrWhiteSpace(_contact.ToEmail) &&
                !recipients.Any(x => string.Equals(x, _contact.ToEmail, StringComparison.OrdinalIgnoreCase)))
            {
                recipients.Add(_contact.ToEmail);
            }

            if (recipients.Count == 0)
            {
                _log.LogDebug("Match email skipped - no recipients configured (user email and Contact:ToEmail missing)");
                return;
            }

            foreach (var recipient in recipients)
            {
                _log.LogInformation("Preparing CV match email to {RecipientEmail}", recipient);

                var msg = new MimeMessage();
                msg.From.Add(MailboxAddress.Parse(_smtp.Username));
                msg.To.Add(MailboxAddress.Parse(recipient));
                msg.Subject = $"[{_environmentName}] {subject}".Trim();

                var builder = new BodyBuilder
                {
                    TextBody = body
                };

                if (!string.IsNullOrWhiteSpace(attachmentPath) && File.Exists(attachmentPath))
                {
                    builder.Attachments.Add(attachmentPath);
                }

                msg.Body = builder.ToMessageBody();

                await SendEmailAsync(msg, "cv match email", ct);
                _log.LogInformation("CV match email sent successfully to {RecipientEmail}", recipient);
            }
        }        

        public static string BuildMatchEmailBody(string cvDocumentId, JobMatchResponse result, string? jobLabel, string? jobSearchLink = null)
        {
            var body = $@"CV Matcher result

CV Document ID: {cvDocumentId}
Job: {jobLabel ?? "N/A"}
Job URL: {result.JobUrl ?? "N/A"}
Score: {result.Score}%

Summary:
{result.Summary}

Strengths:
- {string.Join("\n- ", result.Strengths)}

Gaps:
- {string.Join("\n- ", result.Gaps)}

Recommendations:
- {string.Join("\n- ", result.Recommendations)}";

            if (!string.IsNullOrWhiteSpace(jobSearchLink))
            {
                body += $@"

---
Vrei sa gasesti mai multe joburi potrivite CV-ului tau?
Click: {jobSearchLink}
(link valabil 7 zile)";
            }

            return body;
        }

        public static string BuildMatchEmailSubject(int score, string? jobLabel)
            => $"MyAi.ro CV Match: {score}% - {jobLabel ?? "Job"}";
    }
}
