class ApiKey < ApplicationRecord
  include Encryptable

  belongs_to :user

  # Encrypt display_key if ActiveRecord encryption is configured
  if encryption_ready?
    encrypts :display_key, deterministic: true
  end

  # Constants
  SOURCES = [ "web", "mobile", "monitoring" ].freeze
  DEMO_MONITORING_KEY = "demo_monitoring_key_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"

  # Validations
  validates :display_key, presence: true, uniqueness: true
  validates :name, presence: true
  validates :scopes, presence: true
  validates :source, presence: true, inclusion: { in: SOURCES }
  validate :scopes_not_empty
  validate :one_active_key_per_user_per_source, on: :create

  # Callbacks
  before_validation :set_display_key
  before_destroy :prevent_demo_monitoring_key_destroy!

  # Scopes
  scope :active, -> { where(revoked_at: nil).where("expires_at IS NULL OR expires_at > ?", Time.current) }
  scope :visible, -> { where.not(display_key: DEMO_MONITORING_KEY) }

  # Class methods
  def self.find_by_value(plain_key)
    return nil unless plain_key

    # Find by encrypted display_key (deterministic encryption allows querying)
    find_by(display_key: plain_key)&.tap do |api_key|
      return api_key if api_key.active?
    end
  end

  def self.generate_secure_key
    SecureRandom.hex(32)
  end

  # Instance methods
  def active?
    !revoked? && !expired?
  end

  def revoked?
    revoked_at.present?
  end

  def expired?
    expires_at.present? && expires_at < Time.current
  end

  def key_matches?(plain_key)
    display_key == plain_key
  end

  def revoke!
    raise ActiveRecord::RecordNotDestroyed, "Cannot revoke demo monitoring API key" if demo_monitoring_key?
    update!(revoked_at: Time.current)
  end

  def delete
    raise ActiveRecord::RecordNotDestroyed, "Cannot destroy demo monitoring API key" if demo_monitoring_key?
    super
  end

  def demo_monitoring_key?
    display_key == DEMO_MONITORING_KEY
  end

  def update_last_used!
    update_column(:last_used_at, Time.current)
  end

  # Get the plain text API key for display (automatically decrypted by Rails)
  def plain_key
    display_key
  end

  # Temporarily store the plain key for creation flow
  attr_accessor :key

  private

    def set_display_key
      if key.present?
        self.display_key = key
      end
    end

    def scopes_not_empty
      if scopes.blank? || (scopes.is_a?(Array) && (scopes.empty? || scopes.all?(&:blank?)))
        errors.add(:scopes, "must include at least one permission")
      elsif scopes.is_a?(Array) && scopes.length > 1
        errors.add(:scopes, "can only have one permission level")
      elsif scopes.is_a?(Array) && !%w[read read_write].include?(scopes.first)
        errors.add(:scopes, "must be either 'read' or 'read_write'")
      end
    end

    def one_active_key_per_user_per_source
      if user&.api_keys&.active&.where(source: source)&.where&.not(id: id)&.exists?
        errors.add(:user, "can only have one active API key per source (#{source})")
      end
    end

    def prevent_demo_monitoring_key_destroy!
      return unless demo_monitoring_key?

      errors.add(:base, "Cannot destroy demo monitoring API key")
      throw(:abort)
    end
end
