# frozen_string_literal: true

module RedmineUxToolkit
  module Services
    class OperationalStatus
      Entry = Struct.new(:key, :label_key, :status, :last_at, :next_at, :message, :tab, keyword_init: true)

      class << self
        def entries(settings = Setting.plugin_redmine_ux_toolkit || {})
          [cron_entry, cron_monitor_entry(settings), reminder_entry(settings), recurring_entry, repository_entry(settings), attachment_entry(settings)]
        end

        private

        def cron_entry
          return unavailable(:cron, :label_ux_toolkit_cron, :scheduled_tasks) unless table_exists?('ux_toolkit_cron_states')

          state = UxToolkitCronState.find_by(name: UxToolkitCronState::DEFAULT_NAME)
          Entry.new(
            key: :cron,
            label_key: :label_ux_toolkit_cron,
            status: state_status(state&.last_status, default: :waiting),
            last_at: state&.last_finished_at,
            message: failure_summary(state&.last_status),
            tab: 'scheduled_tasks'
          )
        end

        def cron_monitor_entry(settings)
          return unavailable(:cron_monitoring, :label_ux_toolkit_cron_monitoring, :scheduled_tasks) unless CronMonitor.table_available?
          return disabled(:cron_monitoring, :label_ux_toolkit_cron_monitoring, :scheduled_tasks) unless CronMonitor.enabled?(settings)

          recipient = CronMonitor.monitoring_recipient(
            settings,
            server_admin_email: CronMonitor.server_admin_email
          )
          unless recipient
            return Entry.new(
              key: :cron_monitoring,
              label_key: :label_ux_toolkit_cron_monitoring,
              status: :attention,
              message: I18n.t(:text_ux_toolkit_enabled_configuration_incomplete),
              tab: 'scheduled_tasks'
            )
          end

          state = UxToolkitCronMonitorState.find_by(name: UxToolkitCronMonitorState::DEFAULT_NAME)
          status = case state&.last_status.to_s
                   when 'healthy' then :healthy
                   when 'alerted', 'email_failed', 'check_failed' then :attention
                   else :waiting
                   end

          Entry.new(
            key: :cron_monitoring,
            label_key: :label_ux_toolkit_cron_monitoring,
            status: status,
            last_at: state&.last_checked_at,
            message: state&.last_message,
            tab: 'scheduled_tasks'
          )
        end

        def reminder_entry(settings)
          return unavailable(:reminders, :label_ux_toolkit_issue_reminder_emails, :scheduled_tasks) unless IssueReminderEmails.table_available?
          return disabled(:reminders, :label_ux_toolkit_issue_reminder_emails, :scheduled_tasks) unless IssueReminderEmails.enabled?(settings)

          state = UxToolkitIssueReminderState.default_record
          Entry.new(
            key: :reminders,
            label_key: :label_ux_toolkit_issue_reminder_emails,
            status: state_status(state.last_status, default: :waiting),
            last_at: state.last_successful_at,
            next_at: IssueReminderEmails.next_scheduled_at(settings: settings),
            message: failure_summary(state.last_status),
            tab: 'scheduled_tasks'
          )
        end

        def recurring_entry
          return unavailable(:recurring, :label_ux_toolkit_recurring_issue_rules, :scheduled_tasks) unless table_exists?('ux_toolkit_recurring_issue_rules')

          failed = UxToolkitRecurringIssueRule.enabled.where(last_status: 'failed').exists?
          enabled = UxToolkitRecurringIssueRule.enabled.exists?
          Entry.new(
            key: :recurring,
            label_key: :label_ux_toolkit_recurring_issue_rules,
            status: failed ? :attention : (enabled ? :healthy : :disabled),
            last_at: UxToolkitRecurringIssueRule.maximum(:updated_at),
            next_at: UxToolkitRecurringIssueRule.enabled.minimum(:next_run_on),
            message: failed ? I18n.t(:text_ux_toolkit_operational_failures_present) : nil,
            tab: 'scheduled_tasks'
          )
        end

        def repository_entry(settings)
          return unavailable(:repository, :label_ux_toolkit_repository_refresh, :scheduled_tasks) unless RepositoryRefresh.table_available?
          return disabled(:repository, :label_ux_toolkit_repository_refresh, :scheduled_tasks) unless RepositoryRefresh.enabled?(settings)

          states = UxToolkitRepositoryRefreshState.enabled
          failed = states.where(last_status: UxToolkitRepositoryRefreshState::STATUS_FAILED).exists?
          Entry.new(
            key: :repository,
            label_key: :label_ux_toolkit_repository_refresh,
            status: failed ? :attention : (states.exists? ? :healthy : :waiting),
            last_at: states.maximum(:last_run_at),
            next_at: states.minimum(:next_run_at),
            message: failed ? I18n.t(:text_ux_toolkit_operational_failures_present) : nil,
            tab: 'scheduled_tasks'
          )
        end

        def attachment_entry(settings)
          return unavailable(:attachments, :label_ux_toolkit_attachment_storage, :attachments) unless AttachmentStorage.table_available?
          return disabled(:attachments, :label_ux_toolkit_attachment_storage, :attachments) unless AttachmentStorage.enabled?(settings)

          Entry.new(
            key: :attachments,
            label_key: :label_ux_toolkit_attachment_storage,
            status: :healthy,
            next_at: UxToolkitAttachmentTrashRecord.trashed.minimum(:expires_at),
            message: I18n.t(:text_ux_toolkit_operational_pending_items, count: UxToolkitAttachmentTrashRecord.trashed.count),
            tab: 'attachments'
          )
        end

        def state_status(value, default:)
          case value.to_s
          when 'success' then :healthy
          when 'failed' then :attention
          when 'running' then :running
          else default
          end
        end

        def failure_summary(status)
          I18n.t(:text_ux_toolkit_operational_failures_present) if status.to_s == 'failed'
        end

        def unavailable(key, label_key, tab)
          Entry.new(key: key, label_key: label_key, status: :unavailable, tab: tab)
        end

        def disabled(key, label_key, tab)
          Entry.new(key: key, label_key: label_key, status: :disabled, tab: tab)
        end

        def table_exists?(name)
          ActiveRecord::Base.connection.data_source_exists?(name)
        rescue ActiveRecord::ActiveRecordError
          false
        end
      end
    end
  end
end
