module CrmInsights
  class UpdateService
    def initialize(conversation:, reason: nil)
      @conversation = conversation
      @reason = reason
    end

    def call
      session_stats = ContactSessionCounter.new(@conversation).call
      last_success = @conversation.latest_crm_insight
      last_message_id = relevant_messages.maximum(:id)
      return result_payload(last_success, 'no_messages') if last_message_id.blank?

      from_message_id = last_success&.range_to_message_id ? last_success.range_to_message_id + 1 : nil
      to_message_id = last_message_id
      return result_payload(last_success, 'no_delta') if from_message_id.present? && from_message_id > to_message_id

      result = GenerateService.new(
        conversation: @conversation,
        insight: last_success,
        sessions_count: session_stats[:count],
        last_contact_at: session_stats[:last_contact_at],
        from_message_id: from_message_id,
        to_message_id: to_message_id
      ).generate

      if result[:data].blank?
        create_failed_insight(
          session_stats: session_stats,
          from_message_id: from_message_id,
          to_message_id: to_message_id,
          error_message: result[:error] || 'Falha ao gerar resumo'
        )
        return result_payload(last_success, 'failed', result[:error])
      end

      range_messages = messages_for_range(from_message_id, to_message_id)
      sanitized_result = sanitize_result(
        result[:data],
        range_messages,
        last_success&.structured_data || {},
        @conversation.contact
      )

      insight = create_success_insight(
        result: sanitized_result,
        session_stats: session_stats,
        from_message_id: from_message_id,
        to_message_id: to_message_id
      )
      result_payload(insight, 'success')
    end

    private

    def relevant_messages
      @relevant_messages ||= @conversation.messages.where(
        message_type: %i[incoming outgoing],
        private: false
      )
    end

    def messages_for_range(from_message_id, to_message_id)
      scope = relevant_messages
      scope = scope.where('id >= ?', from_message_id) if from_message_id
      scope = scope.where('id <= ?', to_message_id) if to_message_id
      scope
    end

    def sanitize_result(result, messages, prior_structured, contact)
      structured_data = result['structured_data'] || {}
      incoming_messages = messages.select(&:incoming?)
      incoming_text = incoming_messages.map { |message| message.content.to_s.downcase }.join(' ')
      inbound_count = messages.count(&:incoming?)
      outbound_count = messages.count(&:outgoing?)

      sanitized_structured = structured_data.deep_dup

      return minimal_payload(incoming_messages, contact) if inbound_count < 3

      sanitized_structured['frictions'] = sanitize_frictions(
        structured_data['frictions'],
        incoming_text,
        prior_structured['frictions']
      )
      sanitized_structured['contact_pattern'] = sanitize_contact_pattern(
        structured_data['contact_pattern'],
        incoming_text,
        inbound_count,
        prior_structured['contact_pattern']
      )
      sanitized_structured['preferences'] = sanitize_preferences(
        structured_data['preferences'],
        incoming_text,
        prior_structured['preferences']
      )

      if inbound_count < 3 && outbound_count < 3
        sanitized_structured['intent'] = ''
        sanitized_structured['urgency'] = ''
        sanitized_structured['price_sensitivity'] = ''
        sanitized_structured['commercial_status'] = ''
        sanitized_structured['customer_potential'] = ''
      end

      summary_text = result['summary_text'].to_s.strip
      summary_text = summary_text.presence || 'Ainda nao ha dados suficientes para um perfil do cliente.'

      sanitized_structured['summary_text'] = summary_text
      sanitized_structured['schema_version'] = structured_data['schema_version'] || '1.0'
      sanitized_structured['source'] = structured_data['source'] || 'ai'
      sanitized_structured['generated_at'] = structured_data['generated_at'] || Time.current.iso8601
      sanitized_structured['evidence'] ||= {}

      {
        'summary_text' => summary_text,
        'structured_data' => sanitized_structured
      }
    end

    def sanitize_frictions(frictions, text, prior_frictions)
      items = Array(frictions).map(&:to_s)
      return Array(prior_frictions).map(&:to_s) if items.empty?

      evidence = {
        'pagamento' => /(pagamento|pix|cart[aã]o|forma de pagamento)/i,
        'checkin' => /(check-?in|entrada|hor[aá]rio de entrada)/i,
        'preco' => /(pre[cç]o|valor|custo)/i
      }

      filtered = items.select do |item|
        evidence.any? { |key, pattern| item.downcase.include?(key) && text.match?(pattern) } ||
          evidence.any? { |_, pattern| text.match?(pattern) && item.downcase.match?(pattern) }
      end
      return Array(prior_frictions).map(&:to_s) if filtered.empty? && prior_frictions.present?

      filtered
    end

    def sanitize_contact_pattern(pattern, text, inbound_count, prior_pattern)
      pattern_hash = pattern.is_a?(Hash) ? pattern : {}
      time_range = pattern_hash['time_range'].to_s
      days = Array(pattern_hash['days']).map(&:to_s)

      if inbound_count < 3
        return prior_pattern if prior_pattern.present?

        return { 'time_range' => '', 'days' => [] }
      end

      time_evidence = text.match?(/(\b([01]?\d|2[0-3])h\b|\bmanha\b|\btarde\b|\bnoite\b|\bmadrugada\b)/i)
      day_evidence = text.match?(/\b(segunda|ter[cç]a|quarta|quinta|sexta|sabado|sábado|domingo)\b/i)

      time_range = '' unless time_evidence
      days = [] unless day_evidence
      if days.any?
        normalized_text = text.downcase
        days = days.select do |day|
          normalized_text.match?(/\b#{Regexp.escape(day.downcase)}\b/i)
        end
      end

      {
        'time_range' => time_range,
        'days' => days
      }
    end

    def sanitize_preferences(preferences, text, prior_preferences)
      return Array(prior_preferences).map(&:to_s) if preferences.blank?

      tokens = if preferences.is_a?(Array)
                 preferences
               elsif preferences.is_a?(Hash)
                 preferences.values.flatten
               else
                 [preferences]
               end

      filtered = tokens.map(&:to_s).select do |item|
        case item.downcase
        when /hidro/
          text.include?('hidro')
        when /pix/
          text.include?('pix')
        when /check/
          text.match?(/check-?in/)
        else
          parts = item.downcase.split(/[_\s]/).reject(&:blank?)
          parts.any? { |part| text.include?(part) }
        end
      end
      return Array(prior_preferences).map(&:to_s) if filtered.empty? && prior_preferences.present?

      filtered
    end

    def minimal_summary(text, preferences)
      prefs = Array(preferences).map(&:to_s).reject(&:blank?)
      parts = []

      if prefs.any?
        humanized = prefs.map { |item| item.tr('_', ' ') }
        parts << "demonstrou interesse em #{humanized.join(', ')}"
      end

      parts << 'perguntou sobre pagamento' if text.match?(/pix|pagamento|cart[aã]o|forma de pagamento/i)

      parts << 'perguntou sobre horario de check-in' if text.match?(/check-?in|entrada|hor[aá]rio de entrada/i)

      parts << 'mencionou um dia especifico' if text.match?(/\b(segunda|ter[cç]a|quarta|quinta|sexta|sabado|sábado|domingo)\b/i)

      return 'Conversa inicial, sem historico suficiente para inferir padroes.' if parts.empty?

      "Cliente #{parts.join(' e ')}. Conversa inicial, sem historico suficiente para inferir padroes."
    end

    def minimal_payload(incoming_messages, contact)
      incoming_text = incoming_messages.map { |message| message.content.to_s }.join(' ')
      normalized_text = normalize_text(incoming_text)
      evidence = {}

      preferred_name = contact&.additional_attributes&.fetch('preferred_name', nil)
      if preferred_name.present?
        name_ids = evidence_ids_for(preferred_name, incoming_messages)
        evidence['preferred_name'] = name_ids if name_ids.any?
      end

      room_type = nil
      if normalized_text.include?('hidro')
        room_type = 'suite_hidro'
        evidence['preferences.room_type'] = evidence_ids_for(/hidro/i, incoming_messages)
      end

      day_interest = []
      day_map.each_key do |day|
        day_interest << day if normalized_text.match?(/\b#{day}\b/i)
      end
      if day_interest.any?
        day_regex = Regexp.union(day_interest.map { |day| /\b#{day}\b/i })
        evidence['preferences.date_interest'] = evidence_ids_for(day_regex, incoming_messages)
      end

      intent = nil
      if normalized_text.match?(/reserv|disponibil|vaga|quero|gostaria/)
        intent = 'reserva_rapida'
        evidence['intent'] = evidence_ids_for(/reserv|disponibil|vaga|quero|gostaria/i, incoming_messages)
      end

      summary_text = minimal_summary(normalized_text, room_type ? [room_type] : [])
      summary_text = "Cliente se apresentou como #{preferred_name}. #{summary_text}" if preferred_name.present?
      summary_text = summary_text.strip

      structured_data = {
        'schema_version' => '1.0',
        'source' => 'ai',
        'generated_at' => Time.current.iso8601,
        'summary_text' => summary_text,
        'customer_type' => nil,
        'customer_potential' => nil,
        'intent' => intent,
        'urgency' => nil,
        'price_sensitivity' => nil,
        'confidence' => intent.present? ? 0.9 : nil,
        'preferences' => {
          'room_type' => room_type ? [room_type] : [],
          'date_interest' => day_interest
        },
        'contact_pattern' => nil,
        'frictions' => nil,
        'commercial_status' => nil,
        'nba' => if intent.present?
                   {
                     'action' => 'informar_disponibilidade_e_valor',
                     'priority' => 'media',
                     'reason' => 'Cliente demonstrou interesse inicial, mas ainda nao informou horario nem forma de pagamento.'
                   }
                 end,
        'suggested_labels' => [
          (room_type ? 'hidro' : nil),
          'primeiro_contato'
        ].compact,
        'evidence' => evidence
      }

      {
        'summary_text' => summary_text,
        'structured_data' => structured_data
      }
    end

    def evidence_ids_for(pattern, messages)
      regex = pattern.is_a?(Regexp) ? pattern : /#{Regexp.escape(pattern.to_s)}/i
      messages.select { |message| message.content.to_s.match?(regex) }.map(&:id)
    end

    def normalize_text(value)
      value.to_s.downcase.tr('áàãâéêíóôõúç', 'aaaaeeiooouc')
    end

    def day_map
      {
        'segunda' => 'segunda',
        'terca' => 'terca',
        'quarta' => 'quarta',
        'quinta' => 'quinta',
        'sexta' => 'sexta',
        'sabado' => 'sabado',
        'domingo' => 'domingo'
      }
    end

    def create_success_insight(result:, session_stats:, from_message_id:, to_message_id:)
      structured_data = result['structured_data'] || {}
      model_name = ENV.fetch('CRM_INSIGHTS_MODEL', CrmInsights::GenerateService::DEFAULT_MODEL)
      ConversationCrmInsight.create!(
        conversation: @conversation,
        contact: @conversation.contact,
        account_id: @conversation.account_id,
        summary_text: result['summary_text'],
        structured_data: structured_data,
        contact_sessions_count: session_stats[:count],
        last_contact_at: session_stats[:last_contact_at],
        generated_at: Time.current,
        range_from_message_id: from_message_id,
        range_to_message_id: to_message_id,
        status: 'success',
        schema_version: structured_data['schema_version'] || '1.0',
        model: structured_data['model'] || model_name,
        confidence: structured_data['confidence']
      )
    end

    def create_failed_insight(session_stats:, from_message_id:, to_message_id:, error_message:)
      ConversationCrmInsight.create!(
        conversation: @conversation,
        contact: @conversation.contact,
        account_id: @conversation.account_id,
        summary_text: nil,
        structured_data: {},
        contact_sessions_count: session_stats[:count],
        last_contact_at: session_stats[:last_contact_at],
        generated_at: Time.current,
        range_from_message_id: from_message_id,
        range_to_message_id: to_message_id,
        status: 'failed',
        error_message: error_message
      )
    end

    def result_payload(insight, status, error_message = nil)
      {
        insight: insight,
        status: status,
        error_message: error_message
      }
    end
  end
end
