module UserSearchable
  extend ActiveSupport::Concern

  included do
    include Elasticsearch::Model
    include Elasticsearch::Model::Callbacks
    
    index_name "users_#{Rails.env}"
    
    after_save :update_elasticsearch_index
    after_destroy :delete_from_elasticsearch_index

    settings index: { 
      number_of_shards: 1,
      analysis: {
         normalizer: {
          case_insensitive_normalizer: {
            type: "custom",
            filter: ["lowercase"]
          }
        }
      }
    } do
      mappings dynamic: 'false' do
        indexes :full_name, type: 'text' do
          indexes :keyword, type: 'keyword'
          indexes :sort, type: 'keyword', normalizer: 'case_insensitive_normalizer'  
        end

        indexes :email, type: 'text' do
          indexes :keyword, type: 'keyword' 
          indexes :sort, type: 'keyword', normalizer: 'case_insensitive_normalizer' 
        end

        indexes :role, type: 'text' do
          indexes :keyword, type: 'keyword' 
          indexes :sort, type: 'keyword', normalizer: 'case_insensitive_normalizer'
        end

        indexes :network_access_keyword, type: 'text' do
          indexes :keyword, type: 'keyword' 
          indexes :sort, type: 'keyword', normalizer: 'case_insensitive_normalizer' 
        end

        indexes :network_access, type: 'object' do
          indexes :full, type: 'object' do
            indexes :tags, type: 'keyword'
            indexes :locations, type: 'keyword'
          end
          indexes :limited, type: 'object' do
            indexes :tags, type: 'keyword'
            indexes :locations, type: 'keyword'
          end
        end
  
        indexes :company_name, type: 'keyword'
        indexes :phone_number, type: 'keyword'
        indexes :confirmation_token, type: 'keyword'
        indexes :organisation_id, type: 'integer'
        indexes :role_id, type: 'integer'
        indexes :access_token, type: 'keyword'
        indexes :authorized_networks, type: 'object'
        indexes :approved, type: 'boolean'
        indexes :is_admin, type: 'boolean'
        indexes :mc_token_user, type: 'boolean'
        indexes :created_at, type: 'date'
        indexes :updated_at, type: 'date'
      end
    end
  
    def as_indexed_json(_options = {})
      json = as_json(
        only: [
          :id,
          :full_name,
          :email,
          :organisation_id,
          :phone_number,
          :company_name,
          :confirmation_token,
          :role_id,
          :approved,
          :access_token,
          :is_admin,
          :created_at,
          :updated_at
        ]
      )

      network_access_data = self.network_access_values
      is_admin_user = self.is_admin?
      sortable_network_access = ""
  
      if is_admin_user
        sortable_network_access = "all"
      end
          
      if network_access_data["full"] && network_access_data["full"]["tags"].present?
        sortable_network_access += "#{network_access_data["full"]["tags"].join(", ").downcase}"
      end
  
      if network_access_data["limited"] && network_access_data["limited"]["tags"].present?
        sortable_network_access += "#{network_access_data["limited"]["tags"].join(", ").downcase}"
      end
  
      if network_access_data["full"] && network_access_data["full"]["locations"].present?
        full_location_names = LocationNetwork.where(id: network_access_data["full"]["locations"]).pluck(:network_name)
        network_access_data["full"]["locations"] = full_location_names
        sortable_network_access += " #{full_location_names.join(', ').downcase}"
      end
      
      if network_access_data["limited"] && network_access_data["limited"]["locations"].present?
        limited_location_names = LocationNetwork.where(id: network_access_data["limited"]["locations"]).pluck(:network_name)
        network_access_data["limited"]["locations"] = limited_location_names
        sortable_network_access += " #{limited_location_names.join(', ').downcase}"
      end

      if !is_admin_user && ((network_access_data["full"] && network_access_data["full"]["all"] == true) || (network_access_data["limited"] && network_access_data["limited"]["all"] == true))
        sortable_network_access += " all"
      end
      
      json.merge!(
        'network_access' => network_access_data,
        'authorized_networks' => self.network_tags,
        'role' => self.role.name,
        'network_access_keyword' => sortable_network_access.strip,
        'mc_token_user' => self.machine_token_user? 
      )
      
      json
    end

    def self.count_search_results(query, organisation_id = nil, selector_mode = false, current_user_id = nil, user_id = nil, user_name = nil, include_current = false)
      must_filters = []
      must_not_filters = [
        { term: { mc_token_user: true } },
      ]

      unless include_current
        must_not_filters << { term: { is_admin: "true" } }
        
        if current_user_id.present?
          must_not_filters << { term: { _id: current_user_id.to_s } }
        end
      end
      
      if user_id.present?
        must_filters << { term: { _id: user_id.to_s } }
      elsif user_name.present?
        must_filters << { term: { "full_name.keyword" => user_name } }
      end
      
      must_filters << { term: { organisation_id: organisation_id } } if organisation_id.present?
      
      should_conditions = []
      raw_query = query.to_s.strip.downcase
      
      if (user_id.blank? && user_name.blank?) && raw_query.present?
        searchable_fields = selector_mode ? 
          {
            full_name: { boost_values: { base: 20, phrase: 16, wildcard: 12 } },
            email: { boost_values: { base: 10, phrase: 8, wildcard: 6 } }
          } : 
          {
            full_name: { boost_values: { base: 20, phrase: 16, wildcard: 12 } },
            email: { boost_values: { base: 10, phrase: 8, wildcard: 6 } },
            role: { boost_values: { base: 10, phrase: 8, wildcard: 6 } },
            network_access_keyword: { boost_values: { base: 10, phrase: 8, wildcard: 6 } }
          }

        special_chars = ['_', '@', '.', '/', '\\', '+', '(', ')', '#',  "'", '"', '!', '$', '*', '&', ',']
        has_special_chars = special_chars.any? { |char| raw_query.include?(char) }
        has_hyphen = raw_query.include?("-")
        has_multiple_words = raw_query.include?(" ") && !has_special_chars
        has_colon = raw_query.include?(":")

        searchable_fields.each do |field, config|
          field_str = field.to_s

          if has_colon || has_hyphen
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 3.0 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", case_insensitive: true, boost: config[:boost_values][:wildcard] * 2.5 } } },
            ]
          elsif has_special_chars
            escaped_query = raw_query.gsub('*', '\\*')
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: escaped_query, boost: config[:boost_values][:base] * 1.2 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{escaped_query}*", boost: config[:boost_values][:wildcard] * 1.5, case_insensitive: true } } }         
            ]
          elsif has_multiple_words
            should_conditions += [
              { match_phrase: { field_str => { query: raw_query, boost: config[:boost_values][:phrase] * 1.5 } } },
              { match_phrase_prefix: { field_str => { query: raw_query, boost: config[:boost_values][:phrase] } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", boost: config[:boost_values][:wildcard] * 1.2, case_insensitive: true } } },
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 1.5 } } }
            ]
          else
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 2.0 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", boost: config[:boost_values][:wildcard] * 1.8, case_insensitive: true } } }
            ]
          end
        end

        if !selector_mode && raw_query.length >= 3
          should_conditions << {
            multi_match: {
              query: raw_query,
              fields: ["network_access.full.tags", "network_access.limited.tags"],
              boost: 6
            }
          }
        end
      end
      
      query_definition = if raw_query.present? && user_id.blank? && user_name.blank? && should_conditions.any?
        {
          bool: {
            must: must_filters,
            should: should_conditions,
            must_not: must_not_filters,
            minimum_should_match: 1
          }
        }
      else
        {
          bool: {
            must: must_filters,
            must_not: must_not_filters
          }
        }
      end
      
      count_definition = {
        query: query_definition,
        track_total_hits: true
      }
      
      result = self.__elasticsearch__.search(count_definition)
      result.results.total
    end

    def self.search(query, sort_column, sort_direction, organisation_id = nil, per_page = 10, selector_mode = false, current_user_id = nil, user_id = nil, user_name = nil, search_after = nil, include_current = false)
      start_time = Time.now
      Rails.logger.info("User Pagination Debug - search_after received: #{search_after.inspect}")
    
      # Normalize sort field and create sort definition
      sort_field = case sort_column
                   when "full_name" then "full_name"
                   when "email" then "email"
                   when "role_name" then "role"
                   when "network_access" then "network_access_keyword"
                   else "full_name"
                   end
    
      # Sort definition with _id tiebreaker for search_after
      sort_definition = [
        { "#{sort_field}.sort" => { order: sort_direction } },
        { _id: { order: "asc" } }
      ]
    
      must_filters = []
      must_not_filters = [
        { term: { mc_token_user: true } },
      ]

      unless include_current
        must_not_filters << { term: { is_admin: "true" } }
        
        if current_user_id.present?
          must_not_filters << { term: { _id: current_user_id.to_s } }
        end
      end
    
      # user_id or user_name filter
      if user_id.present?
        must_filters << { term: { _id: user_id.to_s } }
      elsif user_name.present?
        must_filters << { term: { "full_name.keyword" => user_name } }
      end
    
      # other filters
      must_filters << { term: { organisation_id: organisation_id } } if organisation_id.present?
    
      should_conditions = []
      raw_query = query.to_s.strip.downcase
    
       if (user_id.blank? && user_name.blank?) && raw_query.present?
        searchable_fields = selector_mode ? 
          {
            full_name: { boost_values: { base: 20, phrase: 16, wildcard: 12 } },
            email: { boost_values: { base: 10, phrase: 8, wildcard: 6 } }
          } : 
          {
            full_name: { boost_values: { base: 20, phrase: 16, wildcard: 12 } },
            email: { boost_values: { base: 10, phrase: 8, wildcard: 6 } },
            role: { boost_values: { base: 10, phrase: 8, wildcard: 6 } },
            network_access_keyword: { boost_values: { base: 10, phrase: 8, wildcard: 6 } }
          }

        special_chars = ['_', '@', '.', '/', '\\', '+', '(', ')', '#',  "'", '"', '!', '$', '*', '&', ',']
        has_special_chars = special_chars.any? { |char| raw_query.include?(char) }
        has_hyphen = raw_query.include?("-")
        has_multiple_words = raw_query.include?(" ") && !has_special_chars
        has_colon = raw_query.include?(":")

        searchable_fields.each do |field, config|
          field_str = field.to_s

          if has_colon || has_hyphen
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 3.0 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", case_insensitive: true, boost: config[:boost_values][:wildcard] * 2.5 } } },
            ]
          elsif has_special_chars
            escaped_query = raw_query.gsub('*', '\\*')
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: escaped_query, boost: config[:boost_values][:base] * 1.2 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{escaped_query}*", boost: config[:boost_values][:wildcard] * 1.5, case_insensitive: true } } }         
            ]
          elsif has_multiple_words
            should_conditions += [
              { match_phrase: { field_str => { query: raw_query, boost: config[:boost_values][:phrase] * 1.5 } } },
              { match_phrase_prefix: { field_str => { query: raw_query, boost: config[:boost_values][:phrase] } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", boost: config[:boost_values][:wildcard] * 1.2, case_insensitive: true } } },
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 1.5 } } }
            ]
          else
            should_conditions += [
              { term: { "#{field_str}.keyword" => { value: raw_query, boost: config[:boost_values][:base] * 2.0 } } },
              { wildcard: { "#{field_str}.keyword" => { value: "*#{raw_query}*", boost: config[:boost_values][:wildcard] * 1.8, case_insensitive: true } } }
            ]
          end
        end

        if !selector_mode && raw_query.length >= 3
          should_conditions << {
            multi_match: {
              query: raw_query,
              fields: ["network_access.full.tags", "network_access.limited.tags"],
              boost: 6
            }
          }
        end
      end
    
      query_definition = if raw_query.present? && user_id.blank? && user_name.blank? && should_conditions.any?
        {
          bool: {
            must: must_filters,
            should: should_conditions,
            must_not: must_not_filters,
            minimum_should_match: 1
          }
        }
      else
        {
          bool: {
            must: must_filters,
            must_not: must_not_filters
          }
        }
      end
    
      highlight_fields = {}
      if raw_query.present? && user_id.blank? && user_name.blank?
        fields_to_highlight = selector_mode ? ["full_name", "email"] : ["full_name", "email", "role", "network_access_keyword"]
        fields_to_highlight.each { |field| highlight_fields[field] = {} }
    
        unless selector_mode
          highlight_fields["network_access.full.tags"] = {}
          highlight_fields["network_access.limited.tags"] = {}
        end
      end
    
      search_definition = {
        query: query_definition,
        sort: sort_definition,
        size: per_page.to_i
      }
      
      if search_after.present?
        search_definition[:search_after] = search_after
        Rails.logger.info("User Pagination Debug - Using search_after: #{search_after}")
      end
    
      if highlight_fields.any?
        search_definition[:highlight] = {
          pre_tags: ['<strong>'],
          post_tags: ['</strong>'],
          fields: highlight_fields
        }
      end
    
      result = self.__elasticsearch__.search(search_definition)
    
      # Calculate and log search time
      end_time = Time.now
      search_time_ms = ((end_time - start_time) * 1000).round(2)
      Rails.logger.info("User search completed in #{search_time_ms}ms - Query: '#{raw_query}' - Results: #{result.results.size}")
    
      if result.response['hits']['hits'].present?
        last_sort_values = result.response['hits']['hits'].last['sort']
        Rails.logger.info("User Pagination Debug - Next search_after values: #{last_sort_values.inspect}")
      
        result.define_singleton_method(:next_search_after) do
          last_sort_values
        end
      
        has_more = result.results.size >= per_page.to_i
        result.define_singleton_method(:has_more?) do
          has_more
        end
        Rails.logger.info("User Pagination Debug - result.next_search_after: #{result.next_search_after.inspect}")
        Rails.logger.info("User Pagination Debug - result.has_more?: #{result.has_more?.inspect}")
      end
    
      return result
    end
  end

  def update_elasticsearch_index
    __elasticsearch__.index_document
  rescue => e
    Rails.logger.error "Elasticsearch indexing error for User #{id}: #{e.message}"
  end

  def delete_from_elasticsearch_index
    __elasticsearch__.delete_document
  rescue => e
    Rails.logger.error "Elasticsearch delete error for User #{id}: #{e.message}"
  end
end