require 'rails_helper'

RSpec.describe Crm::Leadsquared::ProcessorService do
  let(:account) { create(:account) }
  let(:hook) do
    create(:integrations_hook, :leadsquared, account: account, settings: {
             'access_key' => 'test_access_key',
             'secret_key' => 'test_secret_key',
             'endpoint_url' => 'https://api.leadsquared.com/v2',
             'enable_transcript_activity' => true,
             'enable_conversation_activity' => true,
             'conversation_activity_code' => 1001,
             'transcript_activity_code' => 1002
           })
  end
  let(:contact) { create(:contact, account: account, email: 'test@example.com', phone_number: '+1234567890') }
  let(:contact_with_social_profile) do
    create(:contact, account: account, additional_attributes: { 'social_profiles' => { 'facebook' => 'chatwootapp' } })
  end
  let(:blank_contact) { create(:contact, account: account, email: '', phone_number: '') }
  let(:conversation) { create(:conversation, account: account, contact: contact) }
  let(:service) { described_class.new(hook) }
  let(:lead_client) { instance_double(Crm::Leadsquared::Api::LeadClient) }
  let(:activity_client) { instance_double(Crm::Leadsquared::Api::ActivityClient) }
  let(:lead_finder) { instance_double(Crm::Leadsquared::LeadFinderService) }

  before do
    account.enable_features('crm_integration')
    allow(Crm::Leadsquared::Api::LeadClient).to receive(:new)
      .with('test_access_key', 'test_secret_key', 'https://api.leadsquared.com/v2')
      .and_return(lead_client)
    allow(Crm::Leadsquared::Api::ActivityClient).to receive(:new)
      .with('test_access_key', 'test_secret_key', 'https://api.leadsquared.com/v2')
      .and_return(activity_client)
    allow(Crm::Leadsquared::LeadFinderService).to receive(:new)
      .with(lead_client)
      .and_return(lead_finder)
  end

  describe '.crm_name' do
    it 'returns leadsquared' do
      expect(described_class.crm_name).to eq('leadsquared')
    end
  end

  describe '#handle_contact' do
    context 'when contact is valid' do
      before do
        allow(service).to receive(:identifiable_contact?).and_return(true)
      end

      context 'when contact has no stored lead ID' do
        before do
          contact.update(additional_attributes: { 'external' => nil })
          contact.reload

          allow(lead_client).to receive(:create_or_update_lead)
            .with(any_args)
            .and_return('new_lead_id')
        end

        it 'creates a new lead and stores the ID' do
          service.handle_contact(contact)
          expect(lead_client).to have_received(:create_or_update_lead).with(any_args)
          expect(contact.reload.additional_attributes['external']['leadsquared_id']).to eq('new_lead_id')
        end
      end

      context 'when contact has existing lead ID' do
        before do
          contact.additional_attributes = { 'external' => { 'leadsquared_id' => 'existing_lead_id' } }
          contact.save!

          allow(lead_client).to receive(:update_lead)
            .with(any_args)
            .and_return(nil) # The update method doesn't need to return anything
        end

        it 'updates the lead using existing ID' do
          service.handle_contact(contact)
          expect(lead_client).to have_received(:update_lead).with(any_args)
        end
      end

      context 'when API call raises an error' do
        before do
          allow(lead_client).to receive(:create_or_update_lead)
            .with(any_args)
            .and_raise(Crm::Leadsquared::Api::BaseClient::ApiError.new('API Error'))

          allow(Rails.logger).to receive(:error)
        end

        it 'catches and logs the error' do
          service.handle_contact(contact)
          expect(Rails.logger).to have_received(:error).with(/LeadSquared API error/)
        end
      end
    end

    context 'when contact is invalid' do
      before do
        allow(service).to receive(:identifiable_contact?).and_return(false)
        allow(lead_client).to receive(:create_or_update_lead)
      end

      it 'returns without making API calls' do
        service.handle_contact(blank_contact)
        expect(lead_client).not_to have_received(:create_or_update_lead)
      end
    end
  end

  describe '#handle_conversation_created' do
    let(:activity_note) { 'New conversation started' }

    before do
      allow(Crm::Leadsquared::Mappers::ConversationMapper).to receive(:map_conversation_activity)
        .with(hook, conversation)
        .and_return(activity_note)
    end

    context 'when conversation activities are enabled' do
      before do
        service.instance_variable_set(:@allow_conversation, true)
      end

      context 'when lead_id is found' do
        before do
          allow(lead_finder).to receive(:find_or_create)
            .with(contact)
            .and_return('test_lead_id')

          allow(activity_client).to receive(:post_activity)
            .with('test_lead_id', 1001, activity_note)
            .and_return('test_activity_id')
        end

        it 'creates the activity and stores metadata' do
          service.handle_conversation_created(conversation)
          expect(conversation.reload.additional_attributes['leadsquared']['created_activity_id']).to eq('test_activity_id')
        end
      end

      context 'when post_activity raises an error' do
        before do
          allow(lead_finder).to receive(:find_or_create)
            .with(contact)
            .and_return('test_lead_id')

          allow(activity_client).to receive(:post_activity)
            .with('test_lead_id', 1001, activity_note)
            .and_raise(StandardError.new('Activity error'))

          allow(Rails.logger).to receive(:error)
        end

        it 'logs the error' do
          service.handle_conversation_created(conversation)
          expect(Rails.logger).to have_received(:error).with(/Error creating conversation activity/)
        end
      end
    end

    context 'when conversation activities are disabled' do
      before do
        service.instance_variable_set(:@allow_conversation, false)
        allow(activity_client).to receive(:post_activity)
      end

      it 'does not create an activity' do
        service.handle_conversation_created(conversation)
        expect(activity_client).not_to have_received(:post_activity)
      end
    end
  end

  describe '#handle_conversation_resolved' do
    let(:activity_note) { 'Conversation transcript' }

    before do
      allow(Crm::Leadsquared::Mappers::ConversationMapper).to receive(:map_transcript_activity)
        .with(hook, conversation)
        .and_return(activity_note)
    end

    context 'when transcript activities are enabled and conversation is resolved' do
      before do
        service.instance_variable_set(:@allow_transcript, true)
        conversation.update!(status: 'resolved')

        allow(lead_finder).to receive(:find_or_create)
          .with(contact)
          .and_return('test_lead_id')

        allow(activity_client).to receive(:post_activity)
          .with('test_lead_id', 1002, activity_note)
          .and_return('test_activity_id')
      end

      it 'creates the transcript activity and stores metadata' do
        service.handle_conversation_resolved(conversation)
        expect(conversation.reload.additional_attributes['leadsquared']['transcript_activity_id']).to eq('test_activity_id')
      end
    end

    context 'when conversation is not resolved' do
      before do
        service.instance_variable_set(:@allow_transcript, true)
        conversation.update!(status: 'open')
        allow(activity_client).to receive(:post_activity)
      end

      it 'does not create an activity' do
        service.handle_conversation_resolved(conversation)
        expect(activity_client).not_to have_received(:post_activity)
      end
    end

    context 'when transcript activities are disabled' do
      before do
        service.instance_variable_set(:@allow_transcript, false)
        conversation.update!(status: 'resolved')
        allow(activity_client).to receive(:post_activity)
      end

      it 'does not create an activity' do
        service.handle_conversation_resolved(conversation)
        expect(activity_client).not_to have_received(:post_activity)
      end
    end
  end
end
