# No frozen string literal here
class TranscriptSection < ApplicationRecord
  include WordCloudable
  SCRIPT_PATH = "/home/fareesh/scripts/sprint".freeze

  belongs_to :respondent
  belongs_to :master_section, optional: true

  def ai_summary_markdown
    ApplicationController.helpers.markdown_render(ai_summary)
  end

  def cloud_text
    content
  end

  def fixed_content
    content.split(" ")
    # This regex matches any word (\\b\\w+\\b) that is followed by the same word two or more times
    pattern = /\b(\w+)\b(?:\s+\1){2,}/

    # Replace the repeated words with a single instance of the word
    content.gsub(pattern, '\1')
  end

  def ai_summarize(language: "en", model_map: nil, instruction: nil)
    model_map ||= FeatureModelMap.find_by(feature_name: "SECTION_SUMMARIES")
    instruction ||= Instruction.find_by(key: "INDIVIDUAL_SECTION_SUMMARIES")

    if instruction.nil?
      Rails.logger.fatal("**** MISSING INSTRUCTION for INDIVIDUAL_SECTION_SUMMARIES, section_id=#{id}, respondent_id=#{respondent_id} *****")
      return
    end

    if fixed_content.blank?
      Rails.logger.fatal("**** BLANK fixed_content, section_id=#{id}, respondent_id=#{respondent_id} *****")
      return
    end

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

    prompt = "#{instruction.prompt} <transcript-section>#{fixed_content}</transcript-section>"

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

    if response[:status] != "ok"
      Rails.logger.fatal("**** LLM RESPONSE NOT OK for section_id=#{id}, respondent_id=#{respondent_id}, status=#{response[:status]}, response=#{response.inspect} *****")
      return
    end

    self.ai_summary = response[:data]
    save!
  end

  def self.full_text(respondent)
    if respondent.transcript_file_name.nil?
      respondent.whisper_transcript.verbose_to_text
    else
      TranscriptSection.extract_from_docx(respondent)
    end
  end

  def self.create_from_respondent_transcript(respondent)
    # Delete any existing sections
    respondent.transcript_sections.destroy_all

    full_text = TranscriptSection.full_text(respondent)
    sections = {}
    current_section = nil
    full_text.split("\n").each do |line|
      next unless line

      if line.strip.starts_with?("%%%%%")
        current_section = line.split("%%%%%").last.upcase.strip
        sections[current_section] = ""
      elsif current_section
        sections[current_section] << "#{line}\n"
      end
    end

    sections.each do |section, content|
      TranscriptSection.create(
        respondent:,
        name: section,
        content:
      )
    end
  end

  def self.extract_from_docx(respondent)
    require "shellwords"
    `#{SCRIPT_PATH}/docx_converter.sh #{Shellwords.escape respondent.transcript.path}`
  end

  def self.update_section_name(master_section_id, updated_name)
    TranscriptSection.where(master_section_id:).update!(name: updated_name)
  end

  def self.delete_sections_with_master_id(master_section_id)
    TranscriptSection.where(master_section_id:).destroy_all
  end

  def self.create_from_llm_bookmarks(respondent, raw_llm_response)
    # 1. robustly parse the JSON from the LLM response
    # This regex finds the first opening '{' and the last closing '}'
    # to strip out markdown code blocks or surrounding text.
    json_match = raw_llm_response.match(/\{.*\}/m)
    unless json_match
      Rails.logger.error("No JSON found in LLM response")
      return
    end

    # Sanitize: fix unquoted numeric keys as a safety net
    sanitized_json = json_match[0].gsub(/(\d+):/, '"\1":')

    bookmarks = JSON.parse(sanitized_json)

    # Validate that bookmarks is a hash
    unless bookmarks.is_a?(Hash)
      Rails.logger.error("LLM response is not a valid hash/object")
      return
    end

    # 2. Fetch the source data (Whisper or Docx)
    transcript_data = fetch_transcript_data(respondent)
    return if transcript_data.empty?

    # 3. Sort the offsets numerically (LLM keys are strings like "0", "22")
    sorted_offsets = bookmarks.keys.map(&:to_i).sort

    ActiveRecord::Base.transaction do
      sorted_offsets.each_with_index do |start_offset, index|
        master_section_id = bookmarks[start_offset.to_s].to_i

        # Calculate the end index:
        # If there is a next section, this one ends right before it.
        # If this is the last section, it goes to the end of the transcript.
        next_offset = sorted_offsets[index + 1]
        end_index = next_offset ? (next_offset - 1) : (transcript_data.length - 1)

        # Skip if the range is invalid
        next if start_offset > end_index || master_section_id.zero?

        # Generate the text content for this block
        section_content = generate_content_from_data(transcript_data, start_offset, end_index)

        # Look up the MasterSection name
        master_section = MasterSection.find_by(id: master_section_id)
        unless master_section
          Rails.logger.warn("MasterSection with ID #{master_section_id} not found for offset #{start_offset}")
          next
        end

        TranscriptSection.create!(
          respondent:,
          master_section_id:,
          name: master_section.name,
          offset: start_offset,
          content: section_content
        )
      end
    end
  rescue JSON::ParserError => e
    Rails.logger.error("JSON parsing error in LLM response: #{e.message}")
    Rails.logger.error("Raw response: #{raw_llm_response}")
  rescue StandardError => e
    Rails.logger.fatal("Error generating bookmarks from LLM - #{e.class}: #{e.message}")
    Rails.logger.fatal(e.backtrace.join("\n"))
  end

  def self.fetch_transcript_data(respondent)
    if respondent.whisper_transcript.present?
      respondent.whisper_transcript.verbose_json
    elsif respondent.docx_transcript.present?
      respondent.docx_transcript.verbose_json
    else
      []
    end
  end

  def self.generate_content_from_data(data, start_idx, end_idx)
    # Clamp indices to bounds
    start_idx = [0, start_idx].max
    end_idx = [end_idx, data.length - 1].min

    return "" if start_idx > end_idx

    subset = data[start_idx..end_idx]
    subset.collect do |x|
      "#{x['speaker']}: #{x['text']}"
    end.join("\n\n")
  end
end
