# frozen_string_literal: true

module RedmineUxToolkit
  module Services
    class IssueReminderEmails
      DEFAULT_DAYS_BEFORE_DUE = 7
      DEFAULT_SEND_TIME = '08:00'
      DEFAULT_TIME_ZONE = 'UTC'
      WEEKDAY_KEYS = %w[1 2 3 4 5 6 7].freeze
      STATUS_SKIPPED = 'skipped'

      Result = Struct.new(:status, :message, keyword_init: true)

      class << self
        def enabled?(settings = Setting.plugin_redmine_ux_toolkit || {})
          settings.fetch('enable_issue_reminder_emails', '0').to_s == '1'
        end

        def days_before_due(settings = Setting.plugin_redmine_ux_toolkit || {})
          value = settings['issue_reminder_days_before_due'].to_i
          value.positive? ? value : DEFAULT_DAYS_BEFORE_DUE
        end

        def send_time(settings = Setting.plugin_redmine_ux_toolkit || {})
          value = settings['issue_reminder_send_time'].to_s
          value.match?(/\A(?:[01]\d|2[0-3]):[0-5]\d\z/) ? value : DEFAULT_SEND_TIME
        end

        def weekday_keys(settings = Setting.plugin_redmine_ux_toolkit || {})
          return WEEKDAY_KEYS unless settings.key?('issue_reminder_weekdays')

          Array(settings['issue_reminder_weekdays']).map(&:to_s) & WEEKDAY_KEYS
        end

        def time_zone_name(settings = Setting.plugin_redmine_ux_toolkit || {})
          configured = settings['issue_reminder_time_zone'].to_s
          return configured if valid_time_zone?(configured)

          redmine_default = Setting.respond_to?(:default_users_time_zone) ? Setting.default_users_time_zone.to_s : ''
          return redmine_default if valid_time_zone?(redmine_default)

          rails_default = Time.zone&.name.to_s
          valid_time_zone?(rails_default) ? rails_default : DEFAULT_TIME_ZONE
        end

        def time_zone(settings = Setting.plugin_redmine_ux_toolkit || {})
          ActiveSupport::TimeZone[time_zone_name(settings)] || ActiveSupport::TimeZone[DEFAULT_TIME_ZONE]
        end

        def due_now?(state: reminder_state, now: Time.current, settings: Setting.plugin_redmine_ux_toolkit || {})
          return false unless table_available?
          return false unless enabled?(settings)

          schedule_now = now.in_time_zone(time_zone(settings))
          return false unless weekday_keys(settings).include?(schedule_now.to_date.cwday.to_s)
          return false unless send_time_reached?(schedule_now, settings)
          return false if sent_today?(state, now, settings)

          true
        end

        def send_scheduled(logger: Rails.logger)
          return skipped_result(I18n.t(:text_ux_toolkit_issue_reminder_status_unavailable)) unless table_available?

          state = reminder_state
          unless due_now?(state: state)
            return skipped_result(I18n.t(:text_ux_toolkit_issue_reminders_not_due))
          end

          send_now(logger: logger)
        end

        def send_now(logger: Rails.logger)
          return skipped_result(I18n.t(:text_ux_toolkit_issue_reminder_status_unavailable)) unless table_available?

          state = UxToolkitIssueReminderState.claim!
          unless state
            return Result.new(
              status: 'locked',
              message: I18n.t(:error_ux_toolkit_issue_reminders_already_running)
            )
          end

          days = days_before_due
          deliver_reminders(days)
          message = I18n.t(:notice_ux_toolkit_issue_reminders_sent, days: days)
          state.release_success!(message)
          Result.new(status: 'success', message: message)
        rescue StandardError => e
          logger&.error("[Redmine UX Toolkit] Issue reminder emails failed: #{e.class}: #{e.message}")
          state&.release_failure!("#{e.class}: #{e.message}")
          Result.new(status: 'failed', message: e.message)
        end

        def sent_today?(state = reminder_state, now = Time.current, settings = Setting.plugin_redmine_ux_toolkit || {})
          zone = time_zone(settings)
          state&.last_successful_at&.in_time_zone(zone)&.to_date == now.in_time_zone(zone).to_date
        end

        def next_scheduled_at(now: Time.current, settings: Setting.plugin_redmine_ux_toolkit || {})
          return nil unless enabled?(settings)

          zone = time_zone(settings)
          local_now = now.in_time_zone(zone)
          hour, minute = send_time(settings).split(':').map(&:to_i)

          8.times do |offset|
            date = local_now.to_date + offset
            next unless weekday_keys(settings).include?(date.cwday.to_s)

            candidate = zone.local(date.year, date.month, date.day, hour, minute)
            return candidate if candidate > local_now
          end

          nil
        end

        def table_available?
          ActiveRecord::Base.connection.data_source_exists?('ux_toolkit_issue_reminder_states')
        rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid
          false
        end

        private

        def reminder_state
          table_available? ? UxToolkitIssueReminderState.default_record : nil
        end

        def send_time_reached?(now, settings)
          hour, minute = send_time(settings).split(':').map(&:to_i)
          now.seconds_since_midnight >= (hour.hours + minute.minutes)
        end

        def valid_time_zone?(name)
          name.present? && ActiveSupport::TimeZone[name].present?
        end

        def deliver_reminders(days)
          options = {days: days}

          if Mailer.respond_to?(:with_synched_deliveries)
            Mailer.with_synched_deliveries { Mailer.reminders(options) }
          else
            Mailer.reminders(options)
          end
        end

        def skipped_result(message)
          Result.new(status: STATUS_SKIPPED, message: message)
        end
      end
    end
  end
end
