# frozen_string_literal: true

class MyModuleRepositoriesController < ApplicationController
  include ApplicationHelper

  before_action :load_my_module, except: :assign_my_modules
  before_action :load_repository, except: %i(repositories_dropdown_list repositories_list_html repositories_list create)
  before_action :check_my_module_view_permissions, except: %i(update consume_modal update_consumption assign_my_modules)
  before_action :check_repository_view_permissions, except: %i(index_dt repositories_dropdown_list repositories_list_html repositories_list create)
  before_action :check_repository_row_consumption_permissions, only: %i(consume_modal update_consumption)
  before_action :check_assign_repository_records_permissions, only: %i(update create)
  before_action :load_my_modules, only: :assign_my_modules

  def index_dt
    @draw = params[:draw].to_i
    per_page = params[:length].to_i < 1 ? Constants::REPOSITORY_DEFAULT_PAGE_SIZE : params[:length].to_i
    page = (params[:start].to_i / per_page) + 1
    if params[:simple_view]
      rows_view = 'repository_rows/simple_view_index'
      preload_cells = false
    else
      return render_403 unless can_read_repository?(@repository)

      rows_view = 'repository_rows/index'
      preload_cells = true
    end
    datatable_service = RepositoryDatatableService.new(@repository, params, current_user, @my_module, preload_cells: preload_cells)

    @datatable_params = {
      view_mode: params[:view_mode],
      my_module: @my_module,
      include_stock_consumption: @repository.has_stock_management? && params[:assigned].present?,
      disable_stock_management: true # stock management is always disabled in MyModule context
    }

    @all_rows_count = datatable_service.all_count
    @filtered_rows_count = datatable_service.filtered_count
    @columns_mappings = datatable_service.mappings
    repository_rows = datatable_service.repository_rows
    @repository_rows = repository_rows.page(page).per(per_page)
    render rows_view
  end

  def assign_my_modules
    assigned_count = 0
    skipped_count = 0
    status = :ok

    ActiveRecord::Base.transaction do
      @my_modules.find_each do |my_module|
        service = RepositoryRows::MyModuleAssignUnassignService.call(
          my_module:,
          repository: @repository,
          user: current_user,
          params:
        )
        unless service.succeed?
          status = :unprocessable_entity
          raise ActiveRecord::Rollback
        end
        assigned_count += service.assigned_rows_count
        skipped_count += (params[:rows_to_assign].length - service.assigned_rows_count)
      end
    end

    render json: { assigned_count:, skipped_count: }, status:
  end

  def create
    repository_row = RepositoryRow.find(params[:repository_row_id])
    repository = repository_row.repository
    return render_403 unless can_read_repository?(repository)

    ActiveRecord::Base.transaction do
      @my_module.my_module_repository_rows.create!(repository_row: repository_row, assigned_by: current_user)

      Activities::CreateActivityService.call(activity_type: :assign_repository_record,
                                             owner: current_user,
                                             team: @my_module.experiment.project.team,
                                             project: @my_module.experiment.project,
                                             subject: @my_module,
                                             message_items: { my_module: @my_module.id,
                                                              repository: repository.id,
                                                              record_names: repository_row.name })

      render json: {
        flash: t('my_modules.assigned_items.direct_assign.success')
      }
    end
  rescue StandardError => e
    Rails.logger.error e.message
    render json: {
      flash: t('my_modules.repository.flash.update_error')
    }, status: :bad_request
  end

  def update
    service = RepositoryRows::MyModuleAssignUnassignService.call(my_module: @my_module,
                                                                 repository: @repository,
                                                                 user: current_user,
                                                                 params: params)
    if service.succeed?
      flash = update_flash_message(service)
      status = :ok
    else
      flash = t('my_modules.repository.flash.update_error')
      status = :bad_request
    end

    render json: {
      flash: flash,
      rows_count: @my_module.repository_rows_count(@repository),
      assigned_count: service.assigned_rows_count,
      repository_id: @repository.repository_snapshots.find_by(selected: true)&.id || @repository.id
    }, status: status
  end

  def update_repository_records_modal
    modal = render_to_string(
      partial: 'my_modules/modals/update_repository_records_modal_content',
      locals: { my_module: @my_module,
                repository: @repository,
                selected_rows: params[:selected_rows],
                downstream: params[:downstream] }
    )
    render json: {
      html: modal,
      update_url: my_module_repository_path(@my_module, @repository)
    }, status: :ok
  end

  def assign_repository_records_modal
    modal = render_to_string(
      partial: 'my_modules/modals/assign_repository_records_modal_content',
      locals: { my_module: @my_module,
                repository: @repository,
                selected_rows: params[:selected_rows],
                downstream: params[:downstream] }
    )
    render json: {
      html: modal,
      update_url: my_module_repository_path(@my_module, @repository)
    }, status: :ok
  end

  def repositories_list_html
    @assigned_repositories = @my_module.readable_live_and_snapshot_repositories_list(current_user)
    render json: {
      html: render_to_string(partial: 'my_modules/repositories/repositories_list'),
      assigned_rows_count: @assigned_repositories.map(&:assigned_rows_count).sum
    }
  end

  def repositories_list
    @assigned_repositories = @my_module.live_and_snapshot_repositories_list
    render json: @assigned_repositories, each_serializer: AssignedRepositorySerializer, scope: { user: current_user, my_module: @my_module }
  end

  def full_view_table
    render json: {
      html: render_to_string(
        partial: 'my_modules/repositories/full_view_table',
        locals: { include_stock_consumption: params[:include_stock_consumption] == 'true' }
      )
    }
  end

  def repositories_dropdown_list
    @repositories = Repository.readable_by_user(current_user).joins("
                                LEFT OUTER JOIN repository_rows ON
                                  repository_rows.repository_id = repositories.id
                                LEFT OUTER JOIN my_module_repository_rows ON
                                  my_module_repository_rows.repository_row_id = repository_rows.id
                                  AND my_module_repository_rows.my_module_id = #{@my_module.id}
                              ").select('COUNT(DISTINCT my_module_repository_rows.id) as rows_count, repositories.*')
                              .group(:id)
                              .having('COUNT(my_module_repository_rows.id) > 0 OR repositories.archived = FALSE')
                              .order(:name)

    #render json: { html: render_to_string(partial: 'my_modules/repositories/repositories_dropdown_list') }
    render json: {
      repositories: @repositories.map do |repository|
        {
          id: repository.id,
          name: repository.name,
          rows_count: repository.rows_count,
          shared: repository.shared_with?(current_team),
          table_url: full_view_table_my_module_repository_path(@my_module, repository),
          assign_url: assign_modal_my_module_repository_path(@my_module, repository),
          update_url: update_modal_my_module_repository_path(@my_module, repository),
        }
      end
    }
  end

  def export_repository
    if params[:header_ids]
      RepositoryZipExportJob.perform_later(
        user_id: current_user.id,
        params: {
          repository_id: @repository.id,
          my_module_id: @my_module.id,
          header_ids: params[:header_ids]
        }
      )

      Activities::CreateActivityService.call(
        activity_type: :export_inventory_items_assigned_to_task,
        owner: current_user,
        subject: @my_module,
        team: @repository.team,
        message_items: {
          my_module: @my_module.id,
          repository: @repository.id
        }
      )

      render json: { message: t('zip_export.export_request_success') }, status: :ok
    else
      render json: { message: t('zip_export.export_error') }, status: :unprocessable_entity
    end
  end

  def consume_modal
    @repository_row = @repository.repository_rows.find(params[:row_id])
    @module_repository_row = @my_module.my_module_repository_rows.find_by(repository_row: @repository_row)
    @stock_value = @module_repository_row.repository_row.repository_stock_value
    @html_modal = render_to_string(partial: 'my_modules/repositories/consume_stock_modal_content', formats: [:html])
    render 'my_modules/repositories/consume_modal', formats: [:json]
  end

  def update_consumption
    module_repository_row = @my_module.my_module_repository_rows.find_by(id: params[:module_row_id])

    ActiveRecord::Base.transaction do
      current_stock = module_repository_row.stock_consumption
      module_repository_row.consume_stock(current_user, params[:stock_consumption], params[:comment])

      log_activity(module_repository_row, current_stock, params[:comment])
      protocol_consumption_notification(params[:comment], module_repository_row)
    end

    render json: {}, status: :ok
  end

  private

  def load_my_module
    @my_module = MyModule.find_by(id: params[:my_module_id])
    render_404 unless @my_module
  end

  def load_repository
    @repository = Repository.find_by(id: params[:id])
    render_404 unless @repository
  end

  def load_my_modules
    @my_modules = MyModule.where(id: params[:my_module_ids])

    render_403 unless @my_modules.all? { |my_module| can_assign_my_module_repository_rows?(my_module) }
  end

  def check_my_module_view_permissions
    render_403 unless can_read_my_module?(@my_module)
  end

  def check_repository_view_permissions
    render_403 unless can_read_repository?(@repository)
  end

  def check_assign_repository_records_permissions
    render_403 unless can_assign_my_module_repository_rows?(@my_module)
  end

  def check_repository_row_consumption_permissions
    render_403 unless can_update_my_module_stock_consumption?(@my_module)
  end

  def update_flash_message(service)
    assigned_count = service.assigned_rows_count
    unassigned_count = service.unassigned_rows_count

    if params[:downstream] == 'true'
      if assigned_count && unassigned_count
        t('my_modules.repository.flash.assign_and_unassign_from_task_and_downstream_html',
          assigned_items: assigned_count,
          unassigned_items: unassigned_count)
      elsif assigned_count
        t('my_modules.repository.flash.assign_to_task_and_downstream_html',
          assigned_items: assigned_count)
      elsif unassigned_count
        t('my_modules.repository.flash.unassign_from_task_and_downstream_html',
          unassigned_items: unassigned_count)
      end
    elsif assigned_count && unassigned_count
      t('my_modules.repository.flash.assign_and_unassign_from_task_html',
        assigned_items: assigned_count,
        unassigned_items: unassigned_count)
    elsif assigned_count
      t('my_modules.repository.flash.assign_to_task_html',
        assigned_items: assigned_count)
    elsif unassigned_count
      t('my_modules.repository.flash.unassign_from_task_html',
        unassigned_items: unassigned_count)
    end
  end

  def protocol_consumption_notification(comment, module_repository_row)
    smart_annotation_notification(
      old_text: nil,
      new_text: comment,
      subject: module_repository_row.repository_row,
      title: t('notifications.my_module_consumption_comment_annotation_title',
               repository_item: module_repository_row.repository_row.name,
               repository: @repository.name,
               user: current_user.full_name),
      message: t('notifications.my_module_consumption_comment_annotation_message_html',
                 project: link_to(@my_module.experiment.project.name, project_url(@my_module.experiment.project)),
                 experiment: link_to(@my_module.experiment.name, my_modules_experiment_url(@my_module.experiment)),
                 my_module: link_to(@my_module.name, protocols_my_module_url(@my_module)))
    )
  end

  def log_activity(module_repository_row, stock_consumption_was, comment)
    Activities::CreateActivityService
      .call(activity_type: :task_inventory_item_stock_consumed,
            owner: current_user,
            subject: @my_module,
            team: @repository.team,
            project: @my_module.project,
            message_items: {
              repository: @repository.id,
              repository_row: module_repository_row.repository_row_id,
              stock_consumption_was: stock_consumption_was || 0,
              unit: module_repository_row.repository_row.repository_stock_value.repository_stock_unit_item&.data || '',
              stock_consumption: module_repository_row.stock_consumption || 0,
              my_module: @my_module.id,
              comment: comment
            })
  end
end
