# frozen_string_literal: true

module RedmineUxToolkit
  module Cron
    module Jobs
      class RecurringIssuesJob
        BATCH_SIZE = 20

        def self.perform(context)
          new.perform(context)
        end

        def perform(context)
          return 0 unless UxToolkitRecurringIssueRule.table_exists?

          today = Date.current
          processed = 0

          due_rule_ids(today).each do |rule_id|
            rule = UxToolkitRecurringIssueRule.find_by(id: rule_id)
            next unless rule&.claim!

            process_rule(rule, today, context)
            processed += 1
          end

          context.logger&.info("[redmine_ux_toolkit] Recurring issues job processed #{processed} rule(s).")
          processed
        end

        private

        def due_rule_ids(today)
          UxToolkitRecurringIssueRule
            .due_on(today)
            .where('locked_until IS NULL OR locked_until <= ?', Time.current)
            .order(:next_run_on, :id)
            .limit(BATCH_SIZE)
            .pluck(:id)
        end

        def process_rule(rule, today, context)
          author = rule_author(rule)
          issue = nil

          with_current_user(author) do
            issue = rule.build_issue(author)
            Issue.transaction do
              if issue.save
                rule.release_success!(today, "Created issue ##{issue.id}.")
              else
                rule.release_failure!(issue.errors.full_messages.to_sentence.presence || 'Issue creation failed.')
              end
            end
          end
        rescue StandardError => e
          context.logger&.warn(
            "[redmine_ux_toolkit] Recurring issue rule #{rule.id} failed: #{e.class}: #{e.message}"
          )
          rule.release_failure!("#{e.class}: #{e.message}")
        end

        def rule_author(rule)
          return rule.created_by if rule.created_by&.active?

          User.where(admin: true).detect(&:active?)
        end

        def with_current_user(user)
          previous_user = User.current
          User.current = user if user && User.respond_to?(:current=)
          yield
        ensure
          User.current = previous_user if User.respond_to?(:current=)
        end
      end
    end
  end
end
