module Gekanator
  class QuestionSuggestionAiConverter
    # Temporary heuristic converter for #361.
    # This creates pending ai_generated questions without external LLM calls;
    # accepted questions are still distributed only after admin approval.
    TITLE_LENGTH_RE = /\Aタイトルは\s*(\d+)\s*文字以上[？?]\z/
    ORIGINAL_YEAR_RE = /\Aオリジナルの投稿年は\s*(\d{4})\s*年[？?]\z/
    ORIGINAL_MONTH_RE = /\Aオリジナルの投稿月は\s*(\d{1,2})\s*月[？?]\z/
    ORIGINAL_MONTH_DAY_RE = /\Aオリジナルの投稿日は\s*(\d{1,2})\s*月\s*(\d{1,2})\s*日[？?]\z/
    TITLE_CONTAINS_RE = /\A題名に「(.+?)」が含まれる[？?]\z/
    SOURCE_RE = /\A(.+?)\s+の投稿を思[ひい]浮かべて[ゐい]る[？?]\z/

    def self.call(...) = new(...).call

    def initialize suggestion:, user:
      @suggestion = suggestion
      @user = user
    end

    def call
      suggestion.with_lock do
        existing = existing_generated_question
        return existing if existing

        run = suggestion.gekanator_ai_runs.create!(
            model: 'heuristic_converter_v1',
            status: 'running',
            input_tokens: 0,
            output_tokens: 0,
            estimated_cost_jpy: 0)

        question_attributes = build_question
        question =
            question_attributes &&
            GekanatorQuestion.create!(
                **question_attributes,
                source: 'ai_generated',
                status: 'pending',
                gekanator_question_suggestion: suggestion,
                created_by: user)

        run.update!(status: question ? 'succeeded' : 'failed')
        question
      end
    rescue => error
      run&.update!(status: 'failed') if run&.persisted? && run.status != 'failed'
      raise error
    end

    private

    attr_reader :suggestion, :user

    def existing_generated_question
      suggestion
          .gekanator_questions
          .where(source: 'ai_generated')
          .order(id: :desc)
          .first
    end

    def build_question
      text = normalized_text
      return nil if text.blank?

      structured_question_for(text) || post_similarity_question_for(text)
    end

    def normalized_text
      suggestion.question_text.to_s.gsub(/[[:space:]]+/, ' ').strip
    end

    def structured_question_for text
      case text
      when TITLE_LENGTH_RE
        length = Regexp.last_match(1).to_i
        return nil if length <= 0

        {
          text:,
          kind: 'title',
          condition: {
            type: 'title-length-at-least',
            length:
          },
          priority_weight: 0.95
        }
      when /\A題名に英数字が混じって[ゐい]る[？?]\z/
        {
          text: '題名に英数字が混じってゐる?',
          kind: 'title',
          condition: { type: 'title-has-ascii' },
          priority_weight: 0.95
        }
      when ORIGINAL_YEAR_RE
        year = Regexp.last_match(1).to_i
        {
          text:,
          kind: 'original_date',
          condition: { type: 'original-year', year: },
          priority_weight: 0.95
        }
      when ORIGINAL_MONTH_RE
        month = Regexp.last_match(1).to_i
        return nil unless month.between?(1, 12)

        {
          text:,
          kind: 'original_date',
          condition: { type: 'original-month', month: },
          priority_weight: 0.95
        }
      when ORIGINAL_MONTH_DAY_RE
        month = Regexp.last_match(1).to_i
        day = Regexp.last_match(2).to_i
        return nil unless month.between?(1, 12) && day.between?(1, 31)

        {
          text:,
          kind: 'original_date',
          condition: {
            type: 'original-month-day',
            monthDay: "#{ month }-#{ day }"
          },
          priority_weight: 0.95
        }
      when TITLE_CONTAINS_RE
        title_text = Regexp.last_match(1).to_s.strip
        return nil if title_text.blank?

        {
          text: "題名に「#{ title_text }」が含まれる?",
          kind: 'title',
          condition: { type: 'title-contains', text: title_text },
          priority_weight: 0.95
        }
      when SOURCE_RE
        host = Regexp.last_match(1).to_s.strip
        return nil if host.blank?

        {
          text:,
          kind: 'source',
          condition: { type: 'source', host: },
          priority_weight: 0.95
        }
      else
        nil
      end
    end

    def post_similarity_question_for text
      return nil if suggestion.answer == 'unknown'

      {
        text:,
        kind: 'post_similarity',
        condition: {
          type: 'post-similarity',
          postId: suggestion.gekanator_game.correct_post_id,
          answer: suggestion.answer,
          threshold: 0.65
        },
        priority_weight: 1.0
      }
    end
  end
end
