class PostBulkCreator
  def initialize actor:, posts:, thumbnails:, host: nil
    @actor_id = actor.id
    @posts = posts
    @thumbnails = thumbnails
    @host = host
  end

  def run
    results = Array.new(@posts.length)
    mutex = Mutex.new
    next_index = 0

    workers = Array.new(2) do
      Thread.new do
        Rails.application.executor.wrap do
          ActiveRecord::Base.connection_pool.with_connection do
            actor = User.find(@actor_id)
            loop do
              index = nil
              begin
                index = mutex.synchronize do
                  current = next_index
                  next_index += 1
                  current
                end
                break if index >= @posts.length

                attributes = @posts[index]
                results[index] = create_row(actor, attributes, index)
              rescue StandardError => e
                Rails.logger.error(
                    "post_bulk_creator_worker_failure #{ { error: e.class.name,
                                                          message: e.message,
                                                          index: }.to_json }")
                results[index] = {
                    status: 'failed',
                    recoverable: false,
                    errors: { base: ['登録中にエラーが発生しました．'] },
                    base_errors: [] }
              end
            end
          end
        end
      end
    end
    workers.each(&:join)

    results.each_index do |index|
      next if results[index].present?

      results[index] = {
          status: 'failed',
          recoverable: false,
          errors: { base: ['登録中にエラーが発生しました．'] },
          base_errors: [] }
    end

    { results: }
  end

  private

  def create_row actor, attributes, index
    preflight =
        PostCreatePreflight.new(
            attributes: attributes,
            thumbnail: thumbnail_for(index, attributes),
            host: @host).run
    if preflight[:existing_post_id].present?
      return {
          status: 'skipped',
          existing_post_id: preflight[:existing_post_id],
          existing_post: preflight[:existing_post] }
    end

    post = PostCreator.new(
        actor: actor,
        attributes: normalised_attributes(attributes, preflight, index)).create!
    result = {
        status: 'created',
        post: { id: post.id } }
    result[:field_warnings] = preflight[:field_warnings] if preflight[:field_warnings].present?
    result[:base_warnings] = preflight[:base_warnings] if preflight[:base_warnings].present?
    result
  rescue PostCreatePreflight::ValidationFailed => e
    {
        status: 'failed',
        recoverable: true,
        errors: e.fields,
        base_errors: e.base_errors }
  rescue ActiveRecord::RecordInvalid => e
    existing_post = existing_post_for_race(attributes, e.record)
    if existing_post.present?
      return {
          status: 'skipped',
          existing_post_id: existing_post[:id],
          existing_post: existing_post }
    end

    {
        status: 'failed',
        recoverable: true,
        errors: e.record.errors.to_hash,
        base_errors: e.record.errors[:base] }
  rescue ActiveRecord::RecordNotUnique => e
    if e.message.include?('index_posts_on_url')
      existing_post = existing_post_for_race(attributes)
      return {
          status: 'skipped',
          existing_post_id: existing_post[:id],
          existing_post: existing_post } if existing_post.present?
    end

    Rails.logger.error(
        "post_bulk_creator_record_not_unique #{ { error: e.class.name,
                                                 message: e.message }.to_json }")
    {
        status: 'failed',
        recoverable: false,
        errors: { base: ['登録中にエラーが発生しました．'] },
        base_errors: [] }
  rescue Tag::NicoTagNormalisationError
    {
        status: 'failed',
        recoverable: true,
        errors: { tags: ['ニコニコ・タグは直接指定できません．'] },
        base_errors: [] }
  rescue Tag::DeprecatedTagNormalisationError
    {
        status: 'failed',
        recoverable: true,
        errors: { tags: ['廃止済みタグは付与できません．'] },
        base_errors: [] }
  rescue PostCreator::VideoMsParseError
    {
        status: 'failed',
        recoverable: true,
        errors: { video_ms: ['動画時間の記法が不正です．'] },
        base_errors: [] }
  rescue Post::RemoteThumbnailFetchFailed
    {
        status: 'failed',
        recoverable: true,
        errors: { thumbnail_base: ['サムネイル画像の取得に失敗しました．'] },
        base_errors: [] }
  rescue ArgumentError => e
    {
        status: 'failed',
        recoverable: true,
        errors: { base: [e.message] },
        base_errors: [] }
  rescue StandardError => e
    Rails.logger.error(
        "post_bulk_creator_failure #{ { error: e.class.name,
                                        message: e.message }.to_json }")
    {
        status: 'failed',
        recoverable: false,
        errors: { base: ['登録中にエラーが発生しました．'] },
        base_errors: [] }
  end

  def normalised_attributes attributes, preflight, index
    {
        url: preflight[:url],
        title: preflight[:title],
        thumbnail_base: preflight[:thumbnail_base],
        thumbnail: thumbnail_for(index, attributes),
        tags: preflight[:tags],
        parent_post_ids: preflight[:parent_post_ids],
        original_created_from: preflight[:original_created_from],
        original_created_before: preflight[:original_created_before],
        duration: preflight[:duration],
        video_ms: preflight[:video_ms],
        direct_tag_specs: preflight[:direct_tag_specs],
        default_tag_specs: preflight[:default_tag_specs],
        snapshot_tag_specs: preflight[:snapshot_tag_specs],
        post_tag_specs: preflight[:post_tag_specs],
        tag_sections: preflight[:tag_sections],
        normalised_parent_post_ids: preflight[:normalised_parent_post_ids] }
  end

  def thumbnail_for index, attributes
    return nil if attributes['thumbnail_base'].present? || attributes[:thumbnail_base].present?

    @thumbnails[index]
  end

  def existing_post_for_race attributes, record = nil
    return nil if record.present? && !(record.errors.of_kind?(:url, :taken))

    normal_url = PostUrlNormaliser.normalise(attributes['url'] || attributes[:url])
    return nil if normal_url.blank?

    compact_existing_post(Post.with_attached_thumbnail.find_by(url: normal_url))
  end

  def compact_existing_post post
    PostCompactRepr.base(post, host: @host)
  end
end
