class MaterialSyncSuppression < ApplicationRecord
  SOURCE_KINDS = [
    'uri',
    'google_drive_path',
    'google_drive_path_prefix',
    'google_drive_file',
    'legacy_drive_path',
    'legacy_drive_path_prefix'
  ].freeze

  REASONS = [
    'copyright_high_risk',
    'copyright_takedown',
    'adult_or_sensitive',
    'personal_information',
    'malware_or_dangerous_file',
    'duplicate_or_low_quality',
    'source_owner_request',
    'other'
  ].freeze

  belongs_to :created_by_user, class_name: 'User', optional: true

  before_validation :assign_normalized_source_key

  validates :source_kind, presence: true, inclusion: { in: SOURCE_KINDS }
  validates :reason, presence: true, inclusion: { in: REASONS }
  validates :normalized_source_key, presence: true, uniqueness: true
  validate :source_value_must_be_present
  validate :drive_path_must_be_safe_relative_path

  def self.normalize_source_key source_kind:,
                                source_uri: nil,
                                drive_path: nil,
                                drive_file_id: nil,
                                source_path: nil,
                                source_file_id: nil
    kind = source_kind.to_s
    value =
        case kind
        when 'uri'
          source_uri.to_s.strip
        when 'google_drive_path', 'google_drive_path_prefix',
             'legacy_drive_path', 'legacy_drive_path_prefix'
          (source_path || drive_path).to_s.strip.gsub(%r{/+}, '/')
        when 'google_drive_file'
          (source_file_id || drive_file_id).to_s.strip
        else
          ''
        end

    return nil if kind.blank? || value.blank?

    "#{ kind }:#{ value }"
  end

  private

  def assign_normalized_source_key
    normalize_path_fields!
    self.normalized_source_key =
        self.class.normalize_source_key(source_kind:,
                                        source_uri:,
                                        drive_path:,
                                        drive_file_id:)
  end

  def source_value_must_be_present
    return if normalized_source_key.present?

    errors.add(:base, '同期元を指定してください．')
  end

  def drive_path_must_be_safe_relative_path
    return unless source_kind.in?(
        ['google_drive_path', 'google_drive_path_prefix',
         'legacy_drive_path', 'legacy_drive_path_prefix'])

    value = drive_path.to_s
    return if value.blank?

    if value.start_with?('/') || value.match?(/\A[A-Za-z]:\//)
      errors.add(:drive_path, '相対 path を指定してください．')
    end
    if value.start_with?('My Drive/', 'マイドライブ/')
      errors.add(:drive_path, '同期元フォルダからの相対 path を指定してください．')
    end
    errors.add(:drive_path, 'NUL は使へません．') if value.include?("\0")
    errors.add(:drive_path, '連続スラッシュは使へません．') if value.include?('//')
    errors.add(:drive_path, '末尾スラッシュは使へません．') if value.end_with?('/')

    parts = value.split('/')
    if parts.any? { |part| part.in?(['.', '..']) }
      errors.add(:drive_path, '. や .. は使へません．')
    end
  end

  def normalize_path_fields!
    return unless source_kind.in?(
        ['google_drive_path', 'google_drive_path_prefix',
         'legacy_drive_path', 'legacy_drive_path_prefix'])

    self.drive_path = drive_path.to_s.tr('\\', '/').strip.presence
  end
end
