require 'rails_helper'

RSpec.describe AppliedSla, type: :model do
  describe 'associations' do
    it { is_expected.to belong_to(:sla_policy) }
    it { is_expected.to belong_to(:account) }
    it { is_expected.to belong_to(:conversation) }
  end

  describe 'push_event_data' do
    it 'returns the correct hash' do
      applied_sla = create(:applied_sla)
      expect(applied_sla.push_event_data).to eq(
        {
          id: applied_sla.id,
          sla_id: applied_sla.sla_policy_id,
          sla_status: applied_sla.sla_status,
          created_at: applied_sla.created_at.to_i,
          updated_at: applied_sla.updated_at.to_i,
          sla_description: applied_sla.sla_policy.description,
          sla_name: applied_sla.sla_policy.name,
          sla_first_response_time_threshold: applied_sla.sla_policy.first_response_time_threshold,
          sla_next_response_time_threshold: applied_sla.sla_policy.next_response_time_threshold,
          sla_only_during_business_hours: applied_sla.sla_policy.only_during_business_hours,
          sla_resolution_time_threshold: applied_sla.sla_policy.resolution_time_threshold,
          sla_frt_due_at: applied_sla.frt_due_at,
          sla_nrt_due_at: applied_sla.nrt_due_at,
          sla_rt_due_at: applied_sla.rt_due_at
        }
      )
    end

    it 'shares the working hours cache while serializing due times' do
      account = create(:account)
      inbox = create(:inbox, account: account, working_hours_enabled: true, timezone: 'UTC')
      sla_policy = create(
        :sla_policy,
        account: account,
        first_response_time_threshold: 1.hour,
        next_response_time_threshold: 30.minutes,
        resolution_time_threshold: 2.hours,
        only_during_business_hours: true
      )
      start_time = Time.zone.parse('2024-01-17 10:00:00')
      conversation = create(
        :conversation,
        account: account,
        inbox: inbox,
        created_at: start_time,
        waiting_since: start_time + 1.hour
      )
      conversation.update!(waiting_since: start_time + 1.hour)
      applied_sla = create(:applied_sla, account: account, conversation: conversation, sla_policy: sla_policy)
      working_hours = inbox.working_hours

      expect(working_hours).to receive(:index_by).once.and_call_original

      expect(applied_sla.push_event_data).to include(
        sla_frt_due_at: Time.zone.parse('2024-01-17 11:00:00').to_i,
        sla_nrt_due_at: Time.zone.parse('2024-01-17 11:30:00').to_i,
        sla_rt_due_at: Time.zone.parse('2024-01-17 12:00:00').to_i
      )
    end
  end

  describe 'validates_factory' do
    it 'creates valid applied sla policy object' do
      applied_sla = create(:applied_sla)
      expect(applied_sla.sla_status).to eq 'active'
    end
  end

  describe '.with_sla_applicable_conversation' do
    it 'excludes blocked contacts and keeps conversations with missing contacts' do
      applied_sla = create(:applied_sla)
      blocked_applied_sla = create(:applied_sla)
      missing_contact_applied_sla = create(:applied_sla)

      blocked_applied_sla.conversation.contact.update!(blocked: true)
      missing_contact_applied_sla.conversation.update_columns(contact_id: nil, contact_inbox_id: nil) # rubocop:disable Rails/SkipsModelValidations

      expect(described_class.with_sla_applicable_conversation).to include(applied_sla, missing_contact_applied_sla)
      expect(described_class.with_sla_applicable_conversation).not_to include(blocked_applied_sla)
    end
  end

  describe '#frt_due_at' do
    it 'returns nil when first_response_time_threshold is blank' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(first_response_time_threshold: nil)

      expect(applied_sla.frt_due_at).to be_nil
    end

    it 'returns deadline based on conversation created_at' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(first_response_time_threshold: 3600, only_during_business_hours: false)

      expected_deadline = applied_sla.conversation.created_at.to_i + 3600
      expect(applied_sla.frt_due_at).to eq(expected_deadline)
    end
  end

  describe '#nrt_due_at' do
    it 'returns nil when next_response_time_threshold is blank' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(next_response_time_threshold: nil)

      expect(applied_sla.nrt_due_at).to be_nil
    end

    it 'returns nil when waiting_since is blank' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(next_response_time_threshold: 1800)
      applied_sla.conversation.update!(waiting_since: nil)

      expect(applied_sla.nrt_due_at).to be_nil
    end

    it 'returns deadline based on waiting_since' do
      applied_sla = create(:applied_sla)
      waiting_since = 2.hours.ago
      applied_sla.sla_policy.update!(next_response_time_threshold: 1800, only_during_business_hours: false)
      applied_sla.conversation.update!(waiting_since: waiting_since)

      expected_deadline = waiting_since.to_i + 1800
      expect(applied_sla.nrt_due_at).to eq(expected_deadline)
    end
  end

  describe '#rt_due_at' do
    it 'returns nil when resolution_time_threshold is blank' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(resolution_time_threshold: nil)

      expect(applied_sla.rt_due_at).to be_nil
    end

    it 'returns deadline based on conversation created_at' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(resolution_time_threshold: 7200, only_during_business_hours: false)

      expected_deadline = applied_sla.conversation.created_at.to_i + 7200
      expect(applied_sla.rt_due_at).to eq(expected_deadline)
    end
  end

  describe '#calculate_due_at' do
    it 'uses BusinessHoursService when only_during_business_hours is true' do
      account = create(:account)
      inbox = create(:inbox, account: account, working_hours_enabled: true)
      sla_policy = create(:sla_policy, account: account, first_response_time_threshold: 3600, only_during_business_hours: true)
      conversation = create(:conversation, account: account, inbox: inbox)
      applied_sla = create(:applied_sla, sla_policy: sla_policy, conversation: conversation, account: account)

      expect(Sla::BusinessHoursService).to receive(:new).and_call_original
      applied_sla.frt_due_at
    end

    it 'does not use BusinessHoursService when only_during_business_hours is false' do
      applied_sla = create(:applied_sla)
      applied_sla.sla_policy.update!(first_response_time_threshold: 3600, only_during_business_hours: false)

      expect(Sla::BusinessHoursService).not_to receive(:new)
      applied_sla.frt_due_at
    end
  end
end
