# frozen_string_literal: true

class EmailLog < ActiveRecord::Base
  CRITICAL_EMAIL_TYPES ||=
    Set.new %w[
              account_created
              admin_login
              confirm_new_email
              confirm_old_email
              confirm_old_email_add
              forgot_password
              notify_old_email
              notify_old_email_add
              signup
              signup_after_approval
            ]

  # cf. https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xhtml
  SMTP_ERROR_CODE_REGEXP = Regexp.new(/\d\.\d\.\d+|\d{3}/).freeze

  belongs_to :user
  belongs_to :post
  belongs_to :smtp_group, class_name: "Group"

  validates :email_type, :to_address, presence: true

  scope :bounced, -> { where(bounced: true) }

  scope :addressed_to_user, ->(user) { where(<<~SQL, user_id: user.id) }
      EXISTS(
        SELECT 1
        FROM user_emails
        WHERE user_emails.user_id = :user_id AND
        (email_logs.to_address = user_emails.email OR
         email_logs.cc_addresses ILIKE '%' || user_emails.email || '%')
      )
    SQL

  before_save do
    if self.bounce_error_code.present?
      match = SMTP_ERROR_CODE_REGEXP.match(self.bounce_error_code)
      self.bounce_error_code = match.present? ? match[0] : nil
    end
  end

  after_create do
    # Update last_emailed_at if the user_id is present and email was sent
    User.where(id: user_id).update_all("last_emailed_at = CURRENT_TIMESTAMP") if user_id.present?
  end

  def topic
    @topic ||= self.topic_id.present? ? Topic.find_by(id: self.topic_id) : self.post&.topic
  end

  def self.unique_email_per_post(post, user)
    return yield unless post && user

    DistributedMutex.synchronize("email_log_#{post.id}_#{user.id}") do
      if where(post_id: post.id, user_id: user.id).exists?
        nil
      else
        yield
      end
    end
  end

  def self.reached_max_emails?(user, email_type = nil)
    if SiteSetting.max_emails_per_day_per_user == 0 || CRITICAL_EMAIL_TYPES.include?(email_type)
      return false
    end

    count = where("created_at > ?", 1.day.ago).where(user_id: user.id).count

    count >= SiteSetting.max_emails_per_day_per_user
  end

  def self.count_per_day(start_date, end_date)
    where("created_at BETWEEN ? AND ?", start_date, end_date)
      .group("DATE(created_at)")
      .order("DATE(created_at)")
      .count
  end

  def self.for(reply_key)
    self.find_by(reply_key: reply_key)
  end

  def self.last_sent_email_address
    self.where(email_type: "signup").order(created_at: :desc).limit(1).pluck(:to_address).first
  end

  def bounce_key
    super&.delete("-")
  end

  def cc_users
    return [] if !self.cc_user_ids
    @cc_users ||= User.where(id: self.cc_user_ids)
  end

  def cc_addresses_split
    @cc_addresses_split ||= self.cc_addresses&.split(";") || []
  end

  def as_mail_message
    return if self.raw.blank?
    @mail_message ||= Mail.new(self.raw)
  end

  def raw_headers
    return if self.raw.blank?
    as_mail_message.header.raw_source
  end

  def raw_body
    return if self.raw.blank?
    as_mail_message.body
  end
end

# == Schema Information
#
# Table name: email_logs
#
#  id                        :integer          not null, primary key
#  to_address                :string           not null
#  email_type                :string           not null
#  user_id                   :integer
#  created_at                :datetime         not null
#  updated_at                :datetime         not null
#  post_id                   :integer
#  bounce_key                :uuid
#  bounced                   :boolean          default(FALSE), not null
#  message_id                :string
#  smtp_group_id             :integer
#  cc_addresses              :text
#  cc_user_ids               :integer          is an Array
#  raw                       :text
#  topic_id                  :integer
#  bounce_error_code         :string
#  smtp_transaction_response :string(500)
#  bcc_addresses             :text
#
# Indexes
#
#  index_email_logs_on_bounce_key  (bounce_key) UNIQUE WHERE (bounce_key IS NOT NULL)
#  index_email_logs_on_bounced     (bounced)
#  index_email_logs_on_created_at  (created_at)
#  index_email_logs_on_message_id  (message_id)
#  index_email_logs_on_post_id     (post_id)
#  index_email_logs_on_topic_id    (topic_id) WHERE (topic_id IS NOT NULL)
#  index_email_logs_on_user_id     (user_id)
#
