# frozen_string_literal: true

module RedmineUxToolkit
  module Services
    class ProjectHealth
      STALE_DAYS_DEFAULT = 14
      STALE_DAYS_RANGE = 1..365
      Row = Struct.new(
        :project,
        :open_count,
        :overdue_count,
        :due_today_count,
        :unassigned_count,
        :stale_count,
        :open_checklist_items_count,
        :open_subtasks_count,
        keyword_init: true
      )

      attr_reader :user, :project_ids, :active_projects_only, :include_archived

      def initialize(user:, project_ids: nil, active_projects_only: true, include_archived: false)
        @user = user
        @project_ids = Array(project_ids).compact
        @active_projects_only = active_projects_only
        @include_archived = include_archived
      end

      def rows
        projects = projects_by_id
        return [] if projects.empty?

        ids = projects.keys
        open_scope = open_issue_scope(ids)

        open_counts = grouped_issue_count(open_scope)
        overdue_counts = grouped_issue_count(open_scope.where("#{Issue.table_name}.due_date < ?", today))
        due_today_counts = grouped_issue_count(open_scope.where(due_date: today))
        unassigned_counts = grouped_issue_count(open_scope.where("#{Issue.table_name}.assigned_to_id IS NULL"))
        stale_counts = grouped_issue_count(open_scope.where("#{Issue.table_name}.updated_on < ?", stale_cutoff))
        checklist_counts = open_checklist_item_counts(ids)
        subtask_counts = grouped_issue_count(open_scope.where("#{Issue.table_name}.parent_id IS NOT NULL"))

        projects.values.map do |project|
          Row.new(
            project: project,
            open_count: open_counts[project.id].to_i,
            overdue_count: overdue_counts[project.id].to_i,
            due_today_count: due_today_counts[project.id].to_i,
            unassigned_count: unassigned_counts[project.id].to_i,
            stale_count: stale_counts[project.id].to_i,
            open_checklist_items_count: checklist_counts[project.id].to_i,
            open_subtasks_count: subtask_counts[project.id].to_i
          )
        end
      end

      def stale_days
        settings = Setting.plugin_redmine_ux_toolkit || {}
        days = settings.fetch('stale_issue_days', STALE_DAYS_DEFAULT).to_i

        STALE_DAYS_RANGE.cover?(days) ? days : STALE_DAYS_DEFAULT
      end

      private

      def projects_by_id
        scope = project_scope
        scope = scope.where(id: project_ids) if project_ids.any?
        scope.order(:name).to_a.index_by(&:id)
      end

      def project_scope
        scope = user.admin? ? Project.all : Project.visible(user)

        if active_projects_only
          return active_project_scope(scope)
        end

        include_archived ? scope : without_archived_projects(scope)
      end

      def active_project_scope(scope)
        if scope.respond_to?(:active)
          scope.active
        elsif defined?(Project::STATUS_ACTIVE)
          scope.where(status: Project::STATUS_ACTIVE)
        else
          scope
        end
      end

      def without_archived_projects(scope)
        return scope unless defined?(Project::STATUS_ARCHIVED)

        scope.where.not(status: Project::STATUS_ARCHIVED)
      end

      def open_issue_scope(ids)
        scope = Issue.visible(user).where(project_id: ids)
        closed_status_ids.empty? ? scope : scope.where("#{Issue.table_name}.status_id NOT IN (?)", closed_status_ids)
      end

      def grouped_issue_count(scope)
        scope.group(:project_id).count
      end

      def open_checklist_item_counts(ids)
        return {} unless RedmineUxToolkit::Services::Checklists.enabled?
        return {} unless RedmineUxToolkit::Services::Checklists.checklist_items_table_exists?

        UxToolkitChecklistItem.joins(:issue)
                              .merge(open_issue_scope(ids))
                              .where(is_done: false)
                              .group("#{Issue.table_name}.project_id")
                              .count
      rescue ActiveRecord::ActiveRecordError => e
        Rails.logger.warn("[redmine_ux_toolkit] Could not count project health checklist items: #{e.class.name}")
        {}
      end

      def closed_status_ids
        @closed_status_ids ||= IssueStatus.where(is_closed: true).pluck(:id)
      end

      def stale_cutoff
        Time.zone.now - stale_days.days
      end

      def today
        @today ||= RedmineUxToolkit::Compatibility.user_today(user)
      end
    end
  end
end
