<commit_before>class Document
  include ActiveModel::Model
  include ActiveModel::Validations

  attr_accessor :content_id, :base_path, :title, :summary, :body, :format_specific_fields, :public_updated_at, :state, :bulk_published, :publication_state, :change_note
  attr_writer :change_history, :update_type

  validates :title, presence: true
  validates :summary, presence: true
  validates :body, presence: true, safe_html: true
  validates :update_type, presence: true, if: :live?
  validates :change_note, presence: true, if: :change_note_required?

  COMMON_FIELDS = [
    :title,
    :summary,
    :body,
    :publication_state,
    :public_updated_at,
  ]

  def initialize(params = {}, format_specific_fields = [])
    @content_id = params.fetch(:content_id, SecureRandom.uuid)
    @format_specific_fields = format_specific_fields

    (COMMON_FIELDS + format_specific_fields).each do |field|
      public_send(:"#{field.to_s}=", params.fetch(field, nil))
    end
  end

  def base_path
    @base_path ||= "#{public_path}/#{title.parameterize}"
  end

  def format
    raise NotImplementedError
  end

  def self.format
    raise NotImplementedError
  end

  def phase
    "live"
  end

  def public_path
    raise NoMethodError
  end

  %w{draft live redrafted}.each do |state|
    define_method("#{state}?") do
      publication_state == state
    end
  end

  def published?
    live? || redrafted?
  end

  def change_note_required?
    update_type == 'major' && published?
  end

  def change_history
    @change_history ||= []
  end

  def update_type
    @update_type || "major"
  end

  def users
    content_item.users || []
  end

  def facet_options(facet)
    finder_schema.options_for(facet)
  end

  def organisations
    finder_schema.organisations
  end

  def format_specific_metadata
    format_specific_fields.each_with_object({}) do |f, fields|
      fields[f] = send(f)
    end
  end

  def humanized_attributes
    format_specific_metadata.inject({}) do |attributes, (key, value)|
      humanized_name = finder_schema.humanized_facet_name(key) { key }
      humanized_value = finder_schema.humanized_facet_value(key, value) { value }

      attributes.merge(humanized_name => humanized_value)
    end
  end


  def self.from_publishing_api(payload)
    document = self.new(
      {
        content_id: payload.content_id,
        title: payload.title,
        summary: payload.description,
        body: payload.details.body,
        publication_state: payload.publication_state,
        public_updated_at: payload.public_updated_at
      }
    )

    document.base_path = payload.base_path
    document.update_type = payload.update_type

    # If the document is redrafted remove the last/most
    # recent change note from the change_history array
    # and set it as the document's change note
    document.change_note = payload.details.change_history.pop["note"] if document.redrafted? && payload.details.change_history.length > 1

    # Persist the rest of the change_history on the document
    # if the document is live or redrafted
    document.change_history = payload.details.change_history.map(&:to_h) if document.published?

    document.format_specific_fields.each do |field|
      document.public_send(:"#{field.to_s}=", payload.details.metadata.send(field))
    end

    document
  end

  def public_updated_at
    @public_updated_at ||= Time.zone.now
  end

  def public_updated_at=(timestamp)
    @public_updated_at = Time.parse(timestamp.to_s) unless timestamp.nil?
  end

  def self.all
    response = self.publishing_api.get_content_items(
      content_format: self.format,
      fields: [
        :base_path,
        :content_id,
        :title,
        :public_updated_at,
        :details,
        :description,
      ]
    ).to_ostruct

    response.map { |payload| self.find(payload.content_id) }
  end

  def self.find(content_id)
    self.from_publishing_api(publishing_api.get_content(content_id).to_ostruct)
  end

  def save!
    self.public_updated_at = Time.zone.now

    presented_document = DocumentPresenter.new(self)
    presented_links = DocumentLinksPresenter.new(self)

    begin
      item_request = publishing_api.put_content(self.content_id, presented_document.to_json)
      links_request = publishing_api.put_links(self.content_id, presented_links.to_json)

      item_request.code == 200 && links_request.code == 200
    rescue GdsApi::HTTPErrorResponse => e
      Airbrake.notify(e)

      false
    end
  end

  def publish!
    indexable_document = SearchPresenter.new(self)

    begin
      publish_request = publishing_api.publish(content_id, "major")
      rummager_request = rummager.add_document(
        format,
        base_path,
        indexable_document.to_json,
      )

      publish_request.code == 200 && rummager_request.code == 200
    rescue GdsApi::HTTPErrorResponse => e
      Airbrake.notify(e)
    end
  end

  def rummager
    SpecialistPublisher.services(:rummager)
  end

  def publishing_api
    self.class.publishing_api
  end

  def self.publishing_api
    SpecialistPublisher.services(:publishing_api)
  end

  def finder_schema<commit_msg>Update public_updated_at if update type is major<commit_after>class Document
  include ActiveModel::Model
  include ActiveModel::Validations

  attr_accessor :content_id, :base_path, :title, :summary, :body, :format_specific_fields, :public_updated_at, :state, :bulk_published, :publication_state, :change_note
  attr_writer :change_history, :update_type

  validates :title, presence: true
  validates :summary, presence: true
  validates :body, presence: true, safe_html: true
  validates :update_type, presence: true, if: :live?
  validates :change_note, presence: true, if: :change_note_required?

  COMMON_FIELDS = [
    :title,
    :summary,
    :body,
    :publication_state,
    :public_updated_at,
  ]

  def initialize(params = {}, format_specific_fields = [])
    @content_id = params.fetch(:content_id, SecureRandom.uuid)
    @format_specific_fields = format_specific_fields

    (COMMON_FIELDS + format_specific_fields).each do |field|
      public_send(:"#{field.to_s}=", params.fetch(field, nil))
    end
  end

  def base_path
    @base_path ||= "#{public_path}/#{title.parameterize}"
  end

  def format
    raise NotImplementedError
  end

  def self.format
    raise NotImplementedError
  end

  def phase
    "live"
  end

  def public_path
    raise NoMethodError
  end

  %w{draft live redrafted}.each do |state|
    define_method("#{state}?") do
      publication_state == state
    end
  end

  def published?
    live? || redrafted?
  end

  def change_note_required?
    update_type == 'major' && published?
  end

  def change_history
    @change_history ||= []
  end

  def update_type
    @update_type || "major"
  end

  def users
    content_item.users || []
  end

  def facet_options(facet)
    finder_schema.options_for(facet)
  end

  def organisations
    finder_schema.organisations
  end

  def format_specific_metadata
    format_specific_fields.each_with_object({}) do |f, fields|
      fields[f] = send(f)
    end
  end

  def humanized_attributes
    format_specific_metadata.inject({}) do |attributes, (key, value)|
      humanized_name = finder_schema.humanized_facet_name(key) { key }
      humanized_value = finder_schema.humanized_facet_value(key, value) { value }

      attributes.merge(humanized_name => humanized_value)
    end
  end


  def self.from_publishing_api(payload)
    document = self.new(
      {
        content_id: payload.content_id,
        title: payload.title,
        summary: payload.description,
        body: payload.details.body,
        publication_state: payload.publication_state,
        public_updated_at: payload.public_updated_at
      }
    )

    document.base_path = payload.base_path
    document.update_type = payload.update_type

    # If the document is redrafted remove the last/most
    # recent change note from the change_history array
    # and set it as the document's change note
    document.change_note = payload.details.change_history.pop["note"] if document.redrafted? && payload.details.change_history.length > 1

    # Persist the rest of the change_history on the document
    # if the document is live or redrafted
    document.change_history = payload.details.change_history.map(&:to_h) if document.published?

    document.format_specific_fields.each do |field|
      document.public_send(:"#{field.to_s}=", payload.details.metadata.send(field))
    end

    document
  end

  def public_updated_at
    @public_updated_at ||= Time.zone.now
  end

  def public_updated_at=(timestamp)
    @public_updated_at = Time.parse(timestamp.to_s) unless timestamp.nil?
  end

  def self.all
    response = self.publishing_api.get_content_items(
      content_format: self.format,
      fields: [
        :base_path,
        :content_id,
        :title,
        :public_updated_at,
        :details,
        :description,
      ]
    ).to_ostruct

    response.map { |payload| self.find(payload.content_id) }
  end

  def self.find(content_id)
    self.from_publishing_api(publishing_api.get_content(content_id).to_ostruct)
  end

  def save!
    if self.valid?
      self.public_updated_at = Time.zone.now if self.update_type == 'major'

      presented_document = DocumentPresenter.new(self)
      presented_links = DocumentLinksPresenter.new(self)

      begin
        item_request = publishing_api.put_content(self.content_id, presented_document.to_json)
        links_request = publishing_api.put_links(self.content_id, presented_links.to_json)

        item_request.code == 200 && links_request.code == 200
      rescue GdsApi::HTTPErrorResponse => e
        Airbrake.notify(e)

        false
      end
    else
      raise RecordNotSaved
    end
  end

  class RecordNotSaved < StandardError; end

  def publish!
    indexable_document = SearchPresenter.new(self)

    begin
      publish_request = publishing_api.publish(content_id, "major")
      rummager_request = rummager.add_document(
        format,
        base_path,
        indexable_document.to_json,
      )

      publish_request.code == 200 && rummager_request.code == 200
    rescue GdsApi::HTTPErrorResponse => e
      Airbrake.notify(e)
    end
  end

  def rummager
    SpecialistPublisher.services(:rummager)
  end

  def publishing_api
    self.class.publishing_api
  end

  def self.publishing_api
    SpecialistPublisher.services(:publishing_api)
  end

  def finder_schema
{'fraction_non_alphanumeric': 0.08797178802897446, 'fraction_numerical': 0.0024780785360274493, 'mean_word_length': 3.3812108559498957, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4082401', 'n_tokens_mistral': 3404, 'n_tokens_neox': 3290, 'n_words': 696}