require "async"
require "async/semaphore"

class SectionsReport < ApplicationRecord
  CONCURRENCY_COUNT = 100

  belongs_to :respondent_category
  belongs_to :analysis_report, optional: true
  has_many :section_summaries, as: :groupable, dependent: :destroy

  def respondents
    Respondent.where(id: respondent_ids)
  end

  def selected_sections
    MasterSection.where(id: section_ids)
  end

  def self.aggregate_summaries(id:, task_id:, all_data:)
    Rails.logger.fatal("**** GENERATING AGGREGATE SUMMARIES, ALL DATA -> #{all_data} *****")
    task = SystemTask.find(task_id) if task_id

    sections_report = SectionsReport.find(id) unless all_data
    respondent_category = RespondentCategory.find(id) if all_data

    master_sections = if all_data
                        respondent_category.master_sections
                      else
                        MasterSection.find(sections_report.section_ids)
                      end

    respondent_ids = if all_data
                       respondent_category.respondents.pluck(:id)
                     else
                       sections_report.respondent_ids
                     end

    language = if all_data
                 if respondent_category.has_homogenous_language_respondents
                   respondent_category.respondents.pick(:language).to_sym
                 else
                   :en
                 end
               else
                 sections_report.language.to_sym rescue :en # rubocop:disable Style/RescueModifier
               end

    instruction = Instruction.find_by(key: "AGGREGATE_SUMMARIES")
    model_map = FeatureModelMap.find_by(feature_name: "SECTION_SUMMARIES")

    # Collect work items outside async — pull all data we need from DB upfront
    work_items = master_sections.filter_map do |master_section|
      transcript_sections = TranscriptSection.where(respondent_id: respondent_ids)
                                             .where(master_section_id: master_section.id)
                                             .where.not(ai_summary: nil)

      if transcript_sections.empty?
        Rails.logger.fatal("**** EMPTY TRANSCRIPT SECTIONS FOR #{master_section&.name} *****")
        next
      end

      summaries = transcript_sections.collect { |x| "<summary>#{x.ai_summary}</summary>" }.join("\n")

      {
        master_section_id: master_section.id,
        master_section_name: master_section.name,
        summaries:
      }
    end

    semaphore = Async::Semaphore.new(CONCURRENCY_COUNT)

    Async do
      work_items.each_with_index.map do |item, i|
        Async do
          semaphore.acquire do
            Rails.logger.fatal("** Generating aggregate summary for MasterSection -> #{item[:master_section_name]} ** #{i}")

            system_prompt = instruction.system_prompt.dup
            language_prompt = " The language to be used for the summarization is #{Respondent::LANGUAGES.transform_values(&:titleize)[language]}"
            system_prompt.concat(language_prompt)

            prompt = "#{instruction.prompt} #{item[:summaries]}"
            # Rails.logger.info prompt

            response = LanguageModel.send_with_system_prompt_v2(
              prompt, system_prompt,
              language_model: model_map.llm_model_name,
              provider: model_map.provider
            )

            next unless response[:status] == "ok"

            master_section = MasterSection.find(item[:master_section_id])

            summary = if all_data
                        SectionSummary.find_or_initialize_by(master_section:, groupable: respondent_category, all_data: true)
                      else
                        SectionSummary.find_or_initialize_by(master_section:, groupable: sections_report, all_data: false)
                      end

            summary.update!(summary: response[:data])
          end
        end
      end.map(&:wait)
    end

    task&.set_progress!(progress: 100, org_id: task.user.organization_id, metadata: {
                          all_data: all_data, groupable_id: all_data ? id : sections_report.analysis_report.id, report_type: "section"
                        })
    respondent_category.update!(all_data_section_report_generated: true, requires_section_regeneration: false) if all_data
  end

  def self.summarize_report(section_report_id:, task_id:)
    Rails.logger.info("**** GENERATING SECTION REPORT SUMMARIES *****")

    model_map = FeatureModelMap.find_by(feature_name: "SECTION_SUMMARIES")
    instruction = Instruction.find_by(key: "INDIVIDUAL_SECTION_SUMMARIES")

    section_report = SectionsReport.find(section_report_id)
    task = SystemTask.find(task_id) if task_id

    # Collect all section pairs upfront before entering async context
    work_items = []
    section_report.respondents.each do |respondent|
      sections = respondent.transcript_sections.where(master_section_id: section_report.section_ids)
      sections.each do |section|
        work_items << { section_id: section.id, section_name: section.name, respondent_name: respondent.name, language: section_report&.language }
      end
    end

    # Process in parallel
    semaphore = Async::Semaphore.new(CONCURRENCY_COUNT)

    Async do
      work_items.map do |item|
        Async do
          semaphore.acquire do
            ActiveRecord::Base.connection_pool.with_connection do
              Rails.logger.info("**** GENERATING AI SUMMARY FOR MASTER SECTION - #{item[:section_name]}, RESPONDENT - #{item[:respondent_name]}, LANGUAGE - #{item[:language]} *****")
              section = TranscriptSection.find(item[:section_id])
              section.ai_summarize(language: item[:language], model_map:, instruction:)
            end
          end
        end
      end.map(&:wait)
    end

    task&.set_progress!(org_id: task.user.organization_id, progress: 50, metadata: {
                          groupable_id: section_report.analysis_report.id, all_data: false, report_type: "section"
                        })
    aggregate_summaries(id: section_report_id, task_id: task.id, all_data: false)
  end

  def self.summarize_all_data_report(task_id:, respondent_category_id:)
    Rails.logger.info("**** GENERATING ALL DATA SUMMARIES *****")

    model_map = FeatureModelMap.find_by(feature_name: "SECTION_SUMMARIES")
    instruction = Instruction.find_by(key: "INDIVIDUAL_SECTION_SUMMARIES")

    task = SystemTask.find(task_id) if task_id
    respondent_category = RespondentCategory.find(respondent_category_id)

    language = if respondent_category.has_homogenous_language_respondents
                 respondent_category.respondents.first.transcript_language
               else
                 "en"
               end

    master_sections = respondent_category.master_sections
    transcript_sections = TranscriptSection
                          .where(master_section_id: master_sections.select(:id))
                          .where(respondent_id: respondent_category.respondents.select(:id))

    # Collect ids and language only — avoid passing AR objects into fibers
    work_items = transcript_sections.map { |s| { section_id: s.id, section_name: s.name, respondent_id: s.respondent_id } }

    semaphore = Async::Semaphore.new(CONCURRENCY_COUNT)

    Async do
      work_items.map do |item|
        Async do
          semaphore.acquire do
            ActiveRecord::Base.connection_pool.with_connection do
              Rails.logger.info("**** GENERATING ALL DATA AI SUMMARY FOR MASTER SECTION - #{item[:section_name]}, RESPONDENT ID - #{item[:respondent_id]}, SECTION ID - #{item[:section_id]} *****")
              section = TranscriptSection.find(item[:section_id])

              section.ai_summarize(language:, model_map:, instruction:)
            end
          end
        end
      end.map(&:wait)
    end

    task&.set_progress!(org_id: task.user.organization_id, progress: 50,
                        metadata: { all_data: true, groupable_id: respondent_category_id, report_type: "section" })
    aggregate_summaries(id: respondent_category_id, task_id: task.id, all_data: true)
  end

  def remove_respondent!(respondent_id)
    target_id = respondent_id.to_i
    respondent_ids.delete(target_id)
    save!
    analysis_report.update!(requires_insight_regeneration: true, requires_section_regeneration: true)
  end
end
