# frozen_string_literal: true

# Respondent Categories
class RespondentCategoriesController < ApplicationController
  load_and_authorize_resource :project
  load_and_authorize_resource through: :project

  before_action :set_respondent_category, except: %i[index new create]
  before_action :set_project

  include ExcelSanitizable

  def reset_assistant
    @respondent_category.update(index_name: nil)
    redirect_to assistant_project_path(@project), notice: "Assistant has been reset"
  end

  def assistant_v2
    return redirect_to assistant_project_path(@project) if @respondent_category.index_name.nil?

    @conversations = AssistantConversation.joins(:user).where(raggable: @respondent_category, users: { organization_id: current_user.organization_id })
                                          .order(:created_at).reverse_order
    @conversation = if @conversations.empty?
                      AssistantConversation.create!(raggable: @respondent_category, user: current_user)
                    else
                      params[:conversation_id] ? AssistantConversation.find(params[:conversation_id]) : @conversations.first
                    end
    gon.conversationId = @conversation.id
  end

  def generate_assistant
    @task = SystemTask.where(task_type: "GenerateAssistantV2", status: "running")
                      .where("metadata @> ?",
                             { respondent_category_id: @respondent_category.id }.to_json).last
    if @task.nil?
      status = SystemTask.where(task_class: "LLM", running: true).count.positive? ? "queued" : "running"
      running = status == "running"
      @task = SystemTask.create(
        task_type: "GenerateAssistantV2",
        user_id: current_user.id,
        running:,
        status:,
        metadata: { raggable_type: "RespondentCategory", respondent_category_id: @respondent_category.id, project_id: @project.id }
      )
      ExecuteGenerateAssistantJob.perform_later(@task.id, current_user.id) if running
    end
    redirect_to assistant_status_project_respondent_category_path(@project, @respondent_category, task_id: @task.id),
                notice: "Generating assistant. The process takes time, please be patient."
  end

  def assistant_status
    @task = SystemTask.find(params[:task_id])
  end

  def setup_assistant
    task = SystemTask.where(task_type: "GenerateAssistant").where("metadata @> ?",
                                                                  { respondent_category_id: @respondent_category.id }.to_json).last
    @assistant_generating = task.nil? ? false : %w[queued running].include?(task.status.downcase)

    return if @respondent_category.assistant_id.nil?

    redirect_to assistant_project_respondent_category_path(@project, @respondent_category)
  end

  def clone
    CloneRespondentCategoryJob.perform_later(@respondent_category.id)

    redirect_to project_path(@project),
                notice: "A copy of #{@respondent_category.name} is being created. Please refresh the page after few mins."
  end

  def bulk_queue_transcription
    respondents = @respondent_category.respondents
                                      .where.not(audio_file_file_name: nil)
                                      .where(transcript_file_name: nil)
                                      .where.missing(:whisper_transcript)

    queued_count = 0
    skipped = []

    respondents.each do |respondent|
      next if respondent.transcribing?

      result = respondent.generate_transcript(current_user)

      if result[:success]
        queued_count += 1
      else
        skipped << {
          respondent: respondent,
          reason: result[:reason]
        }
      end
    end

    message = "#{queued_count} transcription tasks queued."

    duration_skips = skipped.select { |s| s[:reason] == :duration_limit_exceeded }
    model_skips = skipped.select { |s| s[:reason] == :model_unsupported }

    if duration_skips.any?
      names = duration_skips.map { |s| s[:respondent].first_name }

      message += " #{duration_skips.size} respondents exceeded the 135-minute limit: #{names.join(', ')}."
    end

    if model_skips.any?
      names = model_skips.map { |s| s[:respondent].first_name }

      message += " #{model_skips.size} respondents using unsupported model: #{names.join(', ')}."
    end

    sleep(1)

    flash[:notice] = message

    redirect_to [@project, @respondent_category]
  end

  def respondent_list_task_status
    respondent_ids = @respondent_category.respondents.pluck(:id)

    # Check Bookmarks
    bookmarks_generating = SystemTask.where(task_type: "GenerateBookmarks", status: %w[queued running])
                                     .where("(metadata->>'respondent_id')::int IN (?)", respondent_ids)
                                     .exists?

    # Check Summaries
    transcripts_summarizing = SystemTask.where(task_type: "TranscriptSummary", status: %w[queued running])
                                        .where("(metadata->>'respondent_id')::int IN (?)", respondent_ids)
                                        .exists?

    render json: {
      bookmarks: bookmarks_generating,
      summaries: transcripts_summarizing
    }, status: :ok
  end

  def download_sections
    require "csv"
    sections = @respondent_category.master_sections.order(:position)
    csv_data = CSV.generate(headers: true) do |csv|
      sections.each do |section|
        csv << [section.name]
      end
    end

    send_data csv_data,
              filename: "#{@respondent_category.name.parameterize}_sections.csv",
              type: "text/csv",
              disposition: "attachment"
  end

  def quantifiable_questions; end

  def quantifiable_answers
    @quantifiable_question = QuantifiableQuestion.find(params[:question_id])
  end

  def submit_quantifiable_question
    qq = QuantifiableQuestion.create(respondent_category_id: @respondent_category.id, question: params[:question],
                                     answer: [])
    qq.queue_task(current_user)
    redirect_to ask_quantifiable_question_project_respondent_category_path(@project, @respondent_category),
                notice: "Your question is queued"
  end

  def create_assistant
    CreateAssistantJob.perform_later(@respondent_category.id, current_user.id)
    redirect_to assistant_project_respondent_category_path(@project, @respondent_category),
                notice: "Assistant Creation Queued. Please check again in 5 mins"
  end

  def generate_all_data_report_summary
    sections_exist = @respondent_category.respondents.joins(:transcript_sections).exists?
    queries_exist = @respondent_category.interview_questions.exists?

    if sections_exist || queries_exist
      AnalysisReport.queue_all_data_report_generation!(
        respondent_category_id: @respondent_category.id,
        queue_insight_generation: queries_exist,
        sections_exist:,
        user_id: current_user.id
      )
      notice = "All Data Report Generation Queued"
    else
      notice = "Cannot generate report. No assigned sections or queries."
    end
    sleep(2.seconds)

    redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category), notice:
  end

  def section_summary
    @transcript_section = @respondent_category.transcript_sections
                                              .where(master_section_id: params[:transcript_section_id]).first
  end

  def section_cloud
    @section = TranscriptSection.find(params[:transcript_section_id])
  end

  def insights_form
    @report_id = params[:report_id].presence
    @report = if @report_id
                InsightsReport.find(@report_id)
              else
                InsightsReport.new
              end
  end

  def section_insights_form
    @report_id = params[:report_id].presence
    @report = if @report_id
                SectionsReport.find(@report_id)
              else
                SectionsReport.new
              end

    @allow_language_change = @respondent_category.has_homogenous_language_respondents
  end

  def theme_reports_form
    @report_id = params[:report_id].presence
    @report = if @report_id
                ThemesReport.find(@report_id)
              else
                ThemesReport.new
              end
  end

  def analysis_report_form
    @no_sections_found = @respondent_category.master_sections.empty?
    @no_queries_found = @respondent_category.interview_questions.empty?

    @allow_language_change = @respondent_category.has_homogenous_language_respondents
  end

  def generate_analysis_report
    # Validate all required fields
    unless @respondent_category.respondents.count.positive?
      return redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category),
                         alert: "There are no respondents to generate insights for"
    end

    assigned_sections_exist = @respondent_category.respondents.joins(:transcript_sections).exists?
    unless assigned_sections_exist
      return redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category),
                         alert: "No sections are assigned to any selected respondents"
    end

    if !params.key?(:tags) && !params.key?(:respondents)
      return redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category),
                         alert: "Please select either tags or respondents"
    end

    if params.key?(:tags)
      tags = params[:tags].map(&:to_i)
      respondents = @respondent_category.respondents.joins(:tags)
                                        .where(tags: { id: tags })
                                        .group("respondents.id")
                                        .having("COUNT(DISTINCT tags.id) = ?", tags.size)
      unless respondents.any?
        return redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category),
                           alert: "There are no respondents with the selected tags"
      end

    elsif params.key?(:respondents)
      respondents = if params[:respondents].include?("all")
                      @respondent_category.respondents
                    else
                      @respondent_category.respondents.where(id: params[:respondents])
                    end
    end

    questions = if params[:questions].include?("all")
                  @respondent_category.interview_questions.pluck(:id)
                else
                  params[:questions].map(&:to_i)
                end

    sections = if params[:sections].include?("all")
                 @respondent_category.master_sections.pluck(:id)
               else
                 params[:sections].map(&:to_i)
               end

    allow_language_change = params[:allow_language_change] == "true"
    language = if allow_language_change
                 # The view controller action makes sure that languages are homogenous, so we can just select any respondents' language
                 @respondent_category.respondents.first.transcript_language
               else
                 "en" # default english
               end

    # Create Reports
    # Analysis Report is the parent. The other reports are children
    analysis_report_params = {
      name: params[:name],
      language:,
      respondent_ids: respondents.pluck(:id),
      section_ids: sections,
      question_ids: questions,
      tag_ids: tags.presence || [],
      respondent_category_id: @respondent_category.id
    }

    analysis_report = AnalysisReport.create!(analysis_report_params)

    sections_report_params = {
      name: params[:name],
      language:,
      respondent_ids: respondents.pluck(:id),
      section_ids: sections,
      tag_ids: tags.presence || [],
      respondent_category_id: @respondent_category.id,
      analysis_report_id: analysis_report.id
    }
    insights_report_params = {
      name: params[:name],
      respondent_ids: respondents.pluck(:id),
      question_ids: questions,
      tag_ids: tags.presence || [],
      respondent_category_id: @respondent_category.id,
      analysis_report_id: analysis_report.id
    }
    themes_report_params = {
      name: params[:name],
      respondent_ids: respondents.pluck(:id),
      section_ids: sections,
      tag_ids: tags.presence || [],
      respondent_category_id: @respondent_category.id,
      analysis_report_id: analysis_report.id
    }

    SectionsReport.create!(sections_report_params)
    InsightsReport.create!(insights_report_params)
    ThemesReport.create!(themes_report_params)

    analysis_report.queue_report_generation!(sections_exist: sections.any?, queue_insight_generation: questions.any?, user_id: current_user.id)
    sleep(2)

    redirect_to analysis_reports_project_respondent_category_path(@project, @respondent_category),
                notice: "Theme Report Generated."
  end

  def sections_list
    gon.master_sections = @respondent_category.master_sections.order(:position)
    gon.respondent_category_id = @respondent_category.id

    task = SystemTask.where(task_type: "GenerateSectionsDG")
                     .where("metadata @> ?", { respondent_category_id: @respondent_category.id }.to_json)
                     .last
    @sections_generating = %w[queued running].include?(task&.status)
  end

  def themes_list
    gon.highlight_themes = @respondent_category.highlight_themes.order(:position)
    gon.project_id = @respondent_category.project_id
    gon.respondent_category_id = @respondent_category.id
  end

  def analysis_reports
    @respondents = @respondent_category.respondents.order(:first_name)
    gon.orgId = current_user.organization_id
  end

  def themes_grid
    @highlight_themes = @respondent_category.highlight_themes
    @respondents_with_highlights = @respondent_category.respondents.distinct
                                                       .joins(:transcript_highlights)
                                                       .includes(:tags)
                                                       .where.not(transcript_highlights: { master_section_id: nil })
                                                       .where(transcript_highlights: {
                                                                highlight_theme_id: @highlight_themes.select(:id)
                                                              })
                                                       .order(:first_name)

    @respondents_with_highlights_raw = @respondent_category.respondents.distinct
                                                           .joins(:transcript_highlights)
                                                           .includes(:tags)
                                                           .where(transcript_highlights: {
                                                                    master_section_id: nil,
                                                                    highlight_theme_id: @highlight_themes.select(:id)
                                                                  })
                                                           .order(:first_name)

    @respondents_with_highlights_unthemed = @respondent_category.respondents.distinct
                                                                .joins(:transcript_highlights)
                                                                .includes(:tags)
                                                                .where.not(transcript_highlights: { master_section_id: nil })
                                                                .where(transcript_highlights: { highlight_theme_id: nil })
                                                                .order(:first_name)

    @raw_highlight_data = @highlight_themes.order(:position).map do |theme|
      highlights_by_respondent = @respondents_with_highlights_raw.map do |respondent|
        highlights = respondent.transcript_highlights
                               .includes(:highlight_theme)
                               .where(
                                 highlight_theme: theme,
                                 master_section_id: nil
                               )
                               .order(created_at: :asc)

        next if highlights.blank?

        {
          respondent_id: respondent.id,
          highlights:
        }
      end.compact # Remove nil entries

      # Only include theme if it has any highlights
      next unless highlights_by_respondent.any?

      {
        theme:,
        highlights_by_respondent:
      }
    end.compact # Remove nil entries

    @highlight_table_data = MasterSection.where(id: TranscriptHighlight.where(highlight_theme: @highlight_themes).select(:master_section_id))
                                         .order(:position)
                                         .map do |section|
      theme_data = @highlight_themes.order(:position).map do |theme|
        highlights_by_respondent = @respondents_with_highlights.map do |respondent|
          highlights = respondent.transcript_highlights
                                 .includes(:highlight_theme, :master_section)
                                 .where(
                                   highlight_theme: theme,
                                   master_section: section
                                 )
                                 .order(created_at: :asc)

          next if highlights.blank?

          {
            respondent_id: respondent.id,
            highlights:
          }
        end.compact # Remove nil entries

        # Only include theme data if there are any highlights
        next unless highlights_by_respondent.any?

        {
          theme:,
          highlights_by_respondent:
        }
      end.compact # Remove nil entries

      # Only include section if it has any themes with highlights
      next unless theme_data.any?

      {
        section:,
        themes_data: theme_data
      }
    end.compact # Remove nil entries

    @unthemed_highlight_table_data = MasterSection
                                     .where(id: TranscriptHighlight.where(highlight_theme_id: nil,
                                                                          respondent_id: @respondent_category.respondents.select(:id))
                                                                            .select(:master_section_id))
                                     .order(:position)
                                     .map do |section|
      highlights_by_respondent = @respondents_with_highlights_unthemed.map do |respondent|
        highlights = respondent.transcript_highlights
                               .includes(:master_section)
                               .where(
                                 highlight_theme_id: nil,
                                 master_section: section
                               )
                               .order(created_at: :asc)

        next if highlights.blank?

        {
          respondent_id: respondent.id,
          highlights:
        }
      end.compact # Remove nil entries

      # Only include section if it has any unthemed highlights
      next unless highlights_by_respondent.any?

      {
        section:,
        highlights_by_respondent:
      }
    end.compact # Remove nil entries
  end

  def full_analysis_report
    # Preload summaries into hashes indexed by their parent IDs
    @section_summaries_by_id = SectionSummary.where(all_data: true, groupable: @respondent_category)
                                             .group_by(&:master_section_id)

    # ==========================================
    # INSIGHTS DATA
    # ==========================================
    # Preload insight summaries using tuple grouping
    @insight_summaries_by_id = InsightSummary.where(all_data: true, groupable: @respondent_category)
                                             .group_by { |s| [s.interview_question_id, s.master_section_id] }

    @insights_respondents = @respondent_category.respondents.includes(:project, :respondent_category, :tags, :interview_insights)
                                                .joins(:interview_insights)
                                                .where(interview_insights: { insights_report_id: nil })
                                                .order(:first_name)
                                                .distinct

    # Build a structured memory hash for the view
    all_report_insights = InterviewInsight.includes(:interview_question, :master_section)
                                          .where(insights_report_id: nil, respondent_id: @insights_respondents.map(&:id))

    @insights_grouped_data = all_report_insights.group_by(&:master_section).map do |section, insights|
      {
        section: section,
        questions_data: insights.group_by(&:interview_question).map do |question, q_insights|
          {
            question: question,
            row_id: "#{question.id}_#{section&.id || 'legacy'}",
            insights_by_respondent: q_insights.index_by(&:respondent_id),
            summary: @insight_summaries_by_id[[question.id, section&.id]]&.last
          }
        end.sort_by { |qd| qd[:question].position || 0 }
      }
    end.sort_by { |g| g[:section]&.position || -1 }

    # Single source of truth for all respondents in this category, preloading associations
    @respondents = @respondent_category.respondents.includes(:transcript_sections, :tags).order(:first_name)

    # ==========================================
    # SECTIONS DATA
    # ==========================================
    if @respondent_category.transcript_sections.present?
      master_sections = @respondent_category.master_sections.order(:position).pluck(:id)

      # Only load sections which are bookmarked in the transcripts
      @sections = @respondent_category.transcript_sections
                                      .where(transcript_sections: { master_section_id: master_sections })
                                      .select("transcript_sections.name, transcript_sections.master_section_id")
                                      .group("transcript_sections.name, transcript_sections.master_section_id")
                                      .order(Arel.sql("array_position(ARRAY[#{master_sections.join(',')}], transcript_sections.master_section_id)"))
                                      .pluck("transcript_sections.name", "transcript_sections.master_section_id")
    end

    # ==========================================
    # THEMES DATA
    # ==========================================
    return unless @respondent_category.highlight_themes.joins(:transcript_highlights).exists?

    @highlight_themes = @respondent_category.highlight_themes

    # Subsets from the single source of truth
    @respondents_with_highlights = @respondents.joins(:transcript_highlights)
                                               .where.not(transcript_highlights: { master_section_id: nil })
                                               .where(transcript_highlights: { highlight_theme_id: @highlight_themes.select(:id) })
                                               .distinct

    @respondents_with_highlights_raw = @respondents.joins(:transcript_highlights)
                                                   .where(transcript_highlights: {
                                                            master_section_id: nil,
                                                            highlight_theme_id: @highlight_themes.select(:id)
                                                          })
                                                   .distinct

    @respondents_with_highlights_unthemed = @respondents.joins(:transcript_highlights)
                                                        .where.not(transcript_highlights: { master_section_id: nil })
                                                        .where(transcript_highlights: { highlight_theme_id: nil })
                                                        .distinct

    # 1. RAW HIGHLIGHTS
    all_raw_highlights = TranscriptHighlight.includes(:highlight_theme)
                                            .where(
                                              respondent_id: @respondents_with_highlights_raw.map(&:id),
                                              master_section_id: nil,
                                              highlight_theme: @highlight_themes
                                            )
                                            .order(created_at: :asc)
                                            .group_by { |th| [th.highlight_theme_id, th.respondent_id] }

    @raw_highlight_data = @highlight_themes.order(:position).map do |theme|
      highlights_by_respondent = @respondents_with_highlights_raw.map do |respondent|
        highlights = all_raw_highlights[[theme.id, respondent.id]] || []
        next if highlights.blank?

        { respondent_id: respondent.id, highlights: }
      end.compact

      next unless highlights_by_respondent.any?

      { theme:, highlights_by_respondent: }
    end.compact

    # 2. THEMED HIGHLIGHTS
    sections_with_highlights = TranscriptHighlight.where(highlight_theme: @highlight_themes).select(:master_section_id)

    all_themed_highlights = TranscriptHighlight.includes(:highlight_theme, :master_section)
                                               .where(
                                                 respondent_id: @respondents_with_highlights.map(&:id),
                                                 highlight_theme: @highlight_themes,
                                                 master_section_id: sections_with_highlights
                                               )
                                               .order(created_at: :asc)
                                               .group_by { |th| [th.master_section_id, th.highlight_theme_id, th.respondent_id] }

    @highlight_table_data = MasterSection.where(id: sections_with_highlights).order(:position).map do |section|
      theme_data = @highlight_themes.order(:position).map do |theme|
        highlights_by_respondent = @respondents_with_highlights.map do |respondent|
          highlights = all_themed_highlights[[section.id, theme.id, respondent.id]] || []
          next if highlights.blank?

          { respondent_id: respondent.id, highlights: }
        end.compact

        next unless highlights_by_respondent.any?

        { theme:, highlights_by_respondent: }
      end.compact

      next unless theme_data.any?

      { section:, themes_data: theme_data }
    end.compact

    # 3. UNTHEMED HIGHLIGHTS
    sections_with_unthemed_highlights = TranscriptHighlight.where(
      highlight_theme_id: nil,
      respondent_id: @respondent_category.respondents.select(:id)
    ).select(:master_section_id)

    all_unthemed_highlights = TranscriptHighlight.includes(:master_section)
                                                 .where(
                                                   respondent_id: @respondents_with_highlights_unthemed.map(&:id),
                                                   highlight_theme_id: nil,
                                                   master_section_id: sections_with_unthemed_highlights
                                                 )
                                                 .order(created_at: :asc)
                                                 .group_by { |th| [th.master_section_id, th.respondent_id] }

    @unthemed_highlight_table_data = MasterSection.where(id: sections_with_unthemed_highlights).order(:position).map do |section|
      highlights_by_respondent = @respondents_with_highlights_unthemed.map do |respondent|
        highlights = all_unthemed_highlights[[section.id, respondent.id]] || []
        next if highlights.blank?

        { respondent_id: respondent.id, highlights: }
      end.compact

      next unless highlights_by_respondent.any?

      { section:, highlights_by_respondent: }
    end.compact
  end

  def sections_view
    @respondents = @respondent_category.respondents.includes(:transcript_sections, :tags).order(:first_name)

    master_sections = @respondent_category.master_sections.order(:position).pluck(:id)

    # Only load sections which are bookmarked in the transcripts
    @sections = @respondent_category.transcript_sections
                                    .where(transcript_sections: { master_section_id: master_sections })
                                    .select("transcript_sections.name, transcript_sections.master_section_id")
                                    .group("transcript_sections.name, transcript_sections.master_section_id")
                                    .order(Arel.sql("array_position(ARRAY[#{master_sections.join(',')}], transcript_sections.master_section_id)"))
                                    .pluck("transcript_sections.name", "transcript_sections.master_section_id")
  end

  def load_section_data
    page = params[:page].to_i
    per_page = 10
    offset = page * per_page

    master_sections = @respondent_category.master_sections.limit(per_page).offset(offset)

    aggregate_summaries = master_sections
                          .joins(:section_summaries)
                          .select("DISTINCT ON (master_sections.id) master_sections.*, section_summaries.*")
                          .order("master_sections.id, section_summaries.created_at DESC")

    sections = @respondent_category.respondent.joins(:transcript_sections)
                                   .where(transcript_sections: { master_section_id: master_sections.pluck(:id) })
                                   .distinct
                                   .select("respondents.id, respondents.first_name, respondents.last_name, transcript_sections.name, transcript_sections.content, transcript_sections.ai_summary")
                                   .order("respondents.first_name")

    render json: { sections:, aggregate_summaries: }
  end

  def download_all_data_report
    require "axlsx"

    p = Axlsx::Package.new

    # ─── SECTIONS SHEETS ──────────────────────────────────────────────────────
    respondents = @respondent_category
                  .respondents
                  .includes(:transcript_sections)
                  .order(:first_name)

    highlight_themes            = @respondent_category.highlight_themes
    respondents_with_highlights = @respondent_category.respondents.distinct
                                                      .joins(:transcript_highlights)
                                                      .where(transcript_highlights: { highlight_theme_id: highlight_themes.select(:id) })

    highlight_table_data = MasterSection
                           .where(id: TranscriptHighlight.where(highlight_theme: highlight_themes).select(:master_section_id))
                           .order(:position)
                           .map do |section|
                             {
                               section:,
                               themes_data: highlight_themes.order(:position).map do |theme|
                                 {
                                   theme:,
                                   highlights_by_respondent: respondents_with_highlights.map do |respondent|
                                     {
                                       respondent_id: respondent.id,
                                       highlights: respondent.transcript_highlights
                                                   .includes(:highlight_theme, :master_section)
                                                   .where(highlight_theme: theme, master_section: section)
                                     }
                                   end
                                 }
                               end
                             }
                           end

    section_header_row   = ["Section"] + respondents.map(&:name)
    highlight_header_row = ["Section | Theme"] + respondents_with_highlights.map(&:name)

    section_rows   = [section_header_row]
    summary_rows   = [section_header_row]
    aggregate_rows = [["Section", "Aggregate Summary"]]
    highlight_rows = [highlight_header_row]

    @respondent_category.master_sections.order(:position).pluck(:id, :name).each do |master_section_id, section_name|
      s_text      = sanitize_and_truncate_for_excel(section_name)
      section_row = [s_text]
      summary_row = [s_text]
      aggr_row    = [s_text]

      respondents.each do |r|
        sections = r.transcript_sections.where(master_section_id:).to_a

        if sections.empty?
          section_row << " NOT FOUND "
          summary_row << " NOT FOUND "
        else
          combined_content = sections.each_with_index.map { |s, i| sections.length > 1 ? "▶ Excerpt #{i + 1}\n#{s.content}" : s.content }.join("\n\n")
          combined_summary = sections.each_with_index.map { |s, i| sections.length > 1 ? "▶ Excerpt #{i + 1}\n#{s.ai_summary}" : s.ai_summary }.join("\n\n")

          section_row << sanitize_and_truncate_for_excel(combined_content)
          summary_row << sanitize_and_truncate_for_excel(combined_summary)
        end
      end

      aggr_summary = SectionSummary.where(master_section_id:, all_data: true, groupable: @respondent_category).last
      aggr_row << (aggr_summary ? sanitize_and_truncate_for_excel(aggr_summary.summary) : " NOT FOUND ")

      section_rows   << section_row
      summary_rows   << summary_row
      aggregate_rows << aggr_row
    end

    highlight_table_data.each do |hs|
      hs[:themes_data].each do |theme_data|
        row = [sanitize_and_truncate_for_excel("#{hs[:section].name} | #{theme_data[:theme].theme}")]
        theme_data[:highlights_by_respondent].each do |rh|
          if rh[:highlights].blank?
            row << "NOT FOUND"
          else
            rh[:highlights].each { |h| row << sanitize_and_truncate_for_excel(h.verbose_to_text) }
          end
        end
        highlight_rows << row
      end
    end

    p.workbook.add_worksheet(name: "Sections") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      section_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    p.workbook.add_worksheet(name: "Section Summaries") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      summary_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    p.workbook.add_worksheet(name: "Section Aggregate Summaries") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      aggregate_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    p.workbook.add_worksheet(name: "Highlights") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      highlight_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    # ─── INSIGHTS SHEETS ──────────────────────────────────────────────────────
    respondents = @respondent_category
                  .respondents
                  .includes(:interview_insights)
                  .joins(:interview_insights)
                  .where(interview_insights: { insights_report_id: nil })
                  .order(:first_name)

    header_row = ["Question"] + respondents.map(&:name)

    insight_rows      = [header_row]
    insight_summ_rows = [header_row]
    insight_aggr_rows = [%w[Question Summary]]

    @respondent_category.interview_questions.includes(:insight_summaries).order(:position).each do |question|
      q_text = sanitize_and_truncate_for_excel(question.question)

      question_row = [q_text]
      summary_row  = [q_text]
      aggr_row     = [
        q_text,
        sanitize_and_truncate_for_excel(
          question.insight_summaries.select { |s| s.groupable == @respondent_category }.last&.summary
        )
      ]

      respondents.each do |r|
        insight = r.interview_insights
                   .select { |x| x.interview_question_id == question.id && x.insights_report_id.nil? }
                   .last

        if insight
          question_row << sanitize_and_truncate_for_excel("\"#{insight.sources}\"")
          summary_row  << sanitize_and_truncate_for_excel(insight.ai_summary)
        else
          question_row << " NOT FOUND "
          summary_row  << " NOT FOUND "
        end
      end

      insight_rows      << question_row
      insight_summ_rows << summary_row
      insight_aggr_rows << aggr_row
    end

    p.workbook.add_worksheet(name: "Insights") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      insight_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    p.workbook.add_worksheet(name: "Insight Summaries") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      insight_summ_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    p.workbook.add_worksheet(name: "Insight Aggregate Summaries") do |sheet|
      style = sheet.styles.add_style(alignment: { wrap_text: true, vertical: :top })
      insight_aggr_rows.each { |row| sheet.add_row row, style: style }
      sheet.column_widths 30, 100
    end

    excel_data = p.to_stream.read
    send_data excel_data,
              filename: "#{@respondent_category.name.parameterize}_all_data_report.xlsx",
              type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  end

  def advanced_analytics; end

  def fetch_reels
    render json: { status: :ok, reels: @respondent_category.reels.as_json(methods: %i[video_url_unsigned reel_available processing]) }
  end

  def reel_creator
    highlights = []
    stitched_reels = @respondent_category.reels.order(:id).as_json(methods: %i[video_url_unsigned audio_url_unsigned reel_available processing])
    @respondents = @respondent_category.respondents.distinct.joins(:transcript_highlights)
    respondents = @respondents.includes(transcript_highlights: :highlight_theme)
                              .joins(:transcript_highlights)
                              .where(transcript_highlights: { manual: false })
                              .as_json(methods: %i[media_type],
                                       include: {
                                         transcript_highlights: {
                                           include: :highlight_theme,
                                           methods: %i[reel_available
                                                       reel_url_unsigned verbose_to_text]
                                         }
                                       })
    processing_reels = SystemTask.where(status: %w[queued running], task_type: "ReelCreation").collect { |x| x.metadata["transcript_highlight_id"] }

    respondents.each do |respondent|
      respondent["transcript_highlights"].each do |highlight|
        highlight["processing"] = processing_reels.include?(highlight["id"])
        highlight["respondent_id"] = respondent["id"]
        highlight["respondent_name"] = respondent["first_name"]
        highlight["media_type"] = respondent["media_type"]
        highlights << highlight if highlight["reel_available"] == true
      end
    end

    gon.respondents = respondents
    gon.highlights = highlights
    gon.stitched_reels = stitched_reels
    gon.any_reels_processing = SystemTask.where(status: %w[queued running], task_type: "StitchedReelCreation")
                                         .where("metadata @> ?",
                                                { respondent_category_id: @respondent_category.id }.to_json)
                                         .exists?

    gon.project_id = @project.id
    gon.respondent_category_id = @respondent_category.id
    gon.orgId = current_user.organization_id
  end

  def stitch_reels
    reel = Reel.create!(respondent_category_id: @respondent_category.id, reel_type: params[:media_type])
    StitchReelJob.perform_later(metadata: {
                                  reel_id: reel.id,
                                  highlight_ids: params[:highlight_ids],
                                  subtitles_enabled: params[:subtitles_enabled]
                                },
                                user_id: current_user.id)
    render json: { status: :ok }
  end

  # GET /respondent_categories or /respondent_categories.json
  def index
    @respondent_categories = @project.respondent_categories.order(:created_at)
  end

  # GET /respondent_categories/1 or /respondent_categories/1.json
  def show
    gon.orgId = current_user.organization_id
    @respondents = @respondent_category.respondents.order(:created_at)
    projects = if current_user.sprint_user?
                 Project.all.includes(:respondent_categories)
               else
                 Project.where(organization_id: gon.orgId).includes(:respondent_categories)
               end
    gon.projects = projects.as_json(
      only: %i[id title],
      include: {
        respondent_categories: {
          only: %i[id name]
        }
      }
    )
    @all_tags = @respondents.unscope(:order).joins(:tags).select("tags.*").distinct.order("tags.name ASC")

    @bookmarks_generating = SystemTask.where(task_type: "GenerateBookmarks", status: %w[queued running])
                                      .where("(metadata->>'respondent_id')::int IN (?)", @respondents.pluck(:id))
                                      .exists?

    @transcripts_summarizing = SystemTask.where(task_type: "TranscriptSummary", status: %w[queued running])
                                         .where("(metadata->>'respondent_id')::int IN (?)", @respondents.pluck(:id))
                                         .exists?
  end

  # GET /respondent_categories/new
  def new
    @respondent_category = @project.respondent_categories.new
  end

  # GET /respondent_categories/1/edit
  def edit; end

  # POST /respondent_categories or /respondent_categories.json
  def create
    @respondent_category = @project.respondent_categories.new(respondent_category_params)

    respond_to do |format|
      if @respondent_category.save
        # Get the latest respondent category, excluding the current one, if any exist
        latest_category = @project.respondent_categories
                                  .where.not(id: @respondent_category.id)
                                  .order(:created_at)
                                  .last

        if latest_category.present?
          latest_category.master_sections.find_each do |section|
            # Create a new duplicate of the section with the correct category ID
            new_section = section.dup
            new_section.respondent_category_id = @respondent_category.id
            new_section.save!
          end
        end
        format.html do
          redirect_to project_respondent_category_url(@project, @respondent_category),
                      notice: "Sub-Project was successfully created."
        end
        format.json { render :show, status: :created, location: [@project, @respondent_category] }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @respondent_category.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /respondent_categories/1 or /respondent_categories/1.json
  def update
    respond_to do |format|
      if @respondent_category.update(respondent_category_params)
        format.html do
          redirect_to project_respondent_category_url(@project, @respondent_category),
                      notice: "Sub-Project was successfully updated."
        end
        format.json { render :show, status: :ok, location: [@project, @respondent_category] }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @respondent_category.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /respondent_categories/1 or /respondent_categories/1.json
  def destroy
    @respondent_category.destroy

    respond_to do |format|
      format.html do
        redirect_to @project,
                    notice: "Sub-Project was successfully deleted."
      end
      format.json { head :no_content }
    end
  end

  private

  def set_project
    @project = Project.find(params[:project_id])
  end

  # Use callbacks to share common setup or constraints between actions.
  def set_respondent_category
    @respondent_category = RespondentCategory.find(params[:id])
  end

  # Only allow a list of trusted parameters through.
  def respondent_category_params
    params.require(:respondent_category).permit(:name, :project_id, :transcript_summary_prompt)
  end
end
