class SummaryDocumentsController < ApplicationController
  before_action :set_ancestors
  before_action :set_summary_document, only: %i[show edit update heartbeat unlock takeover export destroy append_messages]

  skip_before_action :verify_authenticity_token, only: :unlock

  CITATION_MARKER_PATTERN = /[\s,]*[\u2329\u3008\u3010].*?[\u232A\u3009\u3011]/

  def index
    @summary_documents = @respondent_category.summary_documents
  end

  def show; end

  def new
    @summary_document = @respondent_category.summary_documents.build
  end

  def edit
    if @summary_document.locked_by_other?(current_user)
      redirect_to [@project, @respondent_category, @summary_document], alert: "#{@summary_document.locked_by.name} is currently editing this document." and return
    end

    @summary_document.acquire_lock!(current_user)

    gon.summary_document = {
      id: @summary_document.id,
      content_html: @summary_document.content_html,
      lock_version: @summary_document.lock_version
    }
    gon.urls = {
      heartbeat: heartbeat_project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document),
      update: project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document),
      unlock: unlock_project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document),
      showPath: project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document)
    }
  rescue ActiveRecord::StaleObjectError
    redirect_to project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document),
                alert: "Couldn't open the editor right now — please try again."
  end

  def create
    @summary_document = @respondent_category.summary_documents.build(summary_document_params)

    if @summary_document.save
      redirect_to project_respondent_category_summary_documents_path(@project, @respondent_category), notice: "Document created."
    else
      redirect_to project_respondent_category_summary_documents_path(@project, @respondent_category),
                  alert: @summary_document.errors.full_messages.to_sentence
    end
  end

  def update
    render json: { error: "#{@summary_document.locked_by.name} is currently editing this document." }, status: :locked and return if @summary_document.locked_by_other?(current_user)

    @summary_document.assign_attributes(summary_document_params.merge(last_edited_by: current_user))
    @summary_document.save!
    @summary_document.heartbeat!(current_user)
    render json: { lock_version: @summary_document.lock_version, saved_at: @summary_document.updated_at }
  rescue ActiveRecord::StaleObjectError
    @summary_document.reload
    render json: {
      error: "This document was updated by #{@summary_document.last_edited_by&.name} while you were away.",
      current_lock_version: @summary_document.lock_version,
      current_content_html: @summary_document.content_html
    }, status: :conflict
  end

  def destroy
    if @summary_document.locked_by_other?(current_user)
      redirect_to project_respondent_category_summary_documents_path(@project, @respondent_category),
                  alert: "#{@summary_document.locked_by.name} is currently editing this document — can't delete it right now."
      return
    end

    @summary_document.destroy!
    redirect_to project_respondent_category_summary_documents_path(@project, @respondent_category),
                notice: "Document deleted."
  end

  def heartbeat
    @summary_document.heartbeat!(current_user) ? head(:ok) : head(:conflict)
  end

  def unlock
    @summary_document.release_lock!(current_user)
    head :ok
  end

  # Frontend must confirm with the user before calling this — it force-breaks someone else's lock.
  def takeover
    @summary_document.force_release_lock!
    @summary_document.acquire_lock!(current_user)
    redirect_to edit_project_respondent_category_summary_document_path(@project, @respondent_category, @summary_document)
  end

  def export
    html = @summary_document.content_html.to_s
    docx_data = nil

    Tempfile.create(["summary", ".docx"]) do |file|
      _stdout, stderr, status = Open3.capture3("pandoc", "-f", "html", "-t", "docx", "-o", file.path, stdin_data: html)
      raise "Pandoc conversion failed: #{stderr}" unless status.success?

      docx_data = File.binread(file.path)
    end

    send_data docx_data,
              filename: "#{@summary_document.name.parameterize}.docx",
              type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
              disposition: "attachment"
  end

  def append_messages
    render json: { error: "#{@summary_document.locked_by.name} is currently editing this document." }, status: :locked and return if @summary_document.locked_by_other?(current_user)

    message_ids = Array(params[:message_ids]).reject(&:blank?)
    return render json: { error: "No messages selected." }, status: :unprocessable_entity if message_ids.blank?

    # Scope through the user's own org, not just the raw conversation_id param --
    # otherwise a crafted request could pull message_ids from a conversation
    # belonging to a different org into this doc.
    conversation = AssistantConversation.joins(:user)
                                        .where(users: { organization_id: current_user.organization_id })
                                        .find_by(id: params[:conversation_id])
    return render json: { error: "Conversation not found." }, status: :not_found unless conversation

    messages = conversation.assistant_messages.where(id: message_ids).order(:created_at)
    return render json: { error: "Selected messages could not be found." }, status: :unprocessable_entity if messages.empty?

    appended_html = messages.map { |m| render_message_block(m) }.join

    @summary_document.update!(
      content_html: @summary_document.content_html.to_s + appended_html,
      last_edited_by: current_user
    )

    render json: { content_html: @summary_document.content_html }
  end

  private

  def render_message_block(message)
    clean_markdown = strip_citation_markers(message.message.to_s)
    label = message.role == "user" ? "You" : "Assistant"
    body_html = helpers.markdown_render(clean_markdown)
    "<blockquote><p><strong>#{ERB::Util.html_escape(label)}:</strong></p>#{body_html}</blockquote>"
  end

  def strip_citation_markers(text)
    text.gsub(CITATION_MARKER_PATTERN, "")
  end

  def set_ancestors
    @project = Project.find(params[:project_id])
    @respondent_category = RespondentCategory.find(params[:respondent_category_id])
  end

  def set_summary_document
    @summary_document = @respondent_category.summary_documents.find(params[:id])
  end

  def summary_document_params
    params.require(:summary_document).permit(:name, :content_html, :lock_version)
  end
end
