# frozen_string_literal: true

# NetworkRange - Unified IPv4/IPv6 network range management
#
# Uses PostgreSQL's inet type to handle both IPv4 and IPv4 networks seamlessly.
# Provides network intelligence data including ASN, company, geographic info,
# and classification flags (datacenter, proxy, VPN).
class NetworkRange < ApplicationRecord
  # Sources for network range creation
  SOURCES = %w[api_imported user_created manual auto_generated inherited].freeze

  # Associations
  has_many :rules, dependent: :destroy
  belongs_to :user, optional: true

  # Validations
  validates :network, presence: true, uniqueness: true
  validates :source, inclusion: { in: SOURCES }
  validates :asn, numericality: { greater_than: 0 }, allow_blank: true

  # Scopes
  scope :ipv4, -> { where("family(network) = 4") }
  scope :ipv6, -> { where("family(network) = 6") }
  scope :by_country, ->(country) { where(country: country) }
  scope :by_company, ->(company) { where(company: company) }
  scope :by_asn, ->(asn) { where(asn: asn) }
  scope :datacenter, -> { where(is_datacenter: true) }
  scope :proxy, -> { where(is_proxy: true) }
  scope :vpn, -> { where(is_vpn: true) }
  scope :user_created, -> { where(source: 'user_created') }
  scope :api_imported, -> { where(source: 'api_imported') }
  scope :with_events, -> { where("events_count > 0") }
  scope :most_active, -> { order(events_count: :desc) }

  # Callbacks
  before_validation :set_default_source
  # after_save :update_children_inheritance!, if: :should_update_children_inheritance?  # Disabled for now

  # Virtual attribute for CIDR notation
  def cidr
    network.to_s
  end

  def cidr=(new_cidr)
    self.network = new_cidr
  end

  # Network properties
  def prefix_length
    # Get prefix length from IPAddr object
    network.prefix
  end

  def network_address
    # Use PostgreSQL's host function or get from IPAddr object
    network.to_s
  end

  def cidr
    # Return full CIDR notation
    "#{network_address}/#{prefix_length}"
  end

  def broadcast_address
    # Use PostgreSQL's broadcast function
    result = self.class.connection.execute("SELECT broadcast('#{network.to_s}')").first
    result&.values&.first
  end

  def family
    # Check if it's IPv4 or IPv6 by looking at the address
    addr = network.to_s.split('/').first
    addr.include?(':') ? 6 : 4
  end

  def ipv4?
    family == 4
  end

  def ipv6?
    family == 6
  end

  # Network containment and overlap operations
  def contains_ip?(ip_string)
    # Use Postgres >>= operator for containment
    self.class.where("network >>= ?::inet", ip_string).exists?
  rescue => e
    Rails.logger.error "Error checking IP containment: #{e.message}"
    false
  end

  def contains_network?(other_cidr)
    other_network = IPAddr.new(other_cidr)
    network_range = IPAddr.new(network)
    network_range.include?(other_network)
  rescue IPAddr::InvalidAddressError
    false
  end

  def overlaps?(other_cidr)
    network_range = IPAddr.new(network)
    other_network = IPAddr.new(other_cidr)
    network_range.include?(other_network) || other_network.include?(network_range)
  rescue IPAddr::InvalidAddressError
    false
  end

  # Parent/child relationships
  def parent_ranges
    NetworkRange.where("network << ?::inet AND masklen(network) < ?", network.to_s, prefix_length)
                 .order("masklen(network) DESC")
  end

  def child_ranges
    NetworkRange.where("network >> ?::inet AND masklen(network) > ?", network.to_s, prefix_length)
                 .order("masklen(network) ASC")
  end

  def sibling_ranges
    NetworkRange.where("masklen(network) = ?", prefix_length)
                 .where("network && ?::inet", network.to_s)
                 .where.not(id: id)
  end

  # Find nearest parent with intelligence data
  def parent_with_intelligence
    # Use Postgres network operators to find parent ranges directly
    cidr_str = network.to_s
    if cidr_str.include?('/')
      addr_parts = network_address.split('.')
      case addr_parts.length
      when 4 # IPv4
        new_prefix = [prefix_length - 8, 16].max
        parent_cidr = "#{addr_parts[0]}.#{addr_parts[1]}.#{addr_parts[2]}.0/#{new_prefix}"
      else # IPv6 - skip for now
        nil
      end
    else
      nil
    end

    return nil unless parent_cidr

    NetworkRange.where("network <<= ?::inet AND masklen(network) < ?", parent_cidr, prefix_length)
                .where.not(asn: nil)
                .order("masklen(network) DESC")
                .first
  end

  def inherited_intelligence
    return own_intelligence if has_intelligence?

    parent = parent_with_intelligence
    parent ? parent.own_intelligence.merge(inherited: true, parent_cidr: parent.cidr) : {}
  end

  def has_intelligence?
    asn.present? || company.present? || country.present? ||
    is_datacenter? || is_proxy? || is_vpn?
  end

  def own_intelligence
    {
      asn: asn,
      asn_org: asn_org,
      company: company,
      country: country,
      is_datacenter: is_datacenter,
      is_proxy: is_proxy,
      is_vpn: is_vpn,
      inherited: false,
      source: source
    }
  end

  # Geographic lookup
  def geo_lookup_country!
    return if country.present?

    sample_ip = network_address
    geo_country = GeoIpService.lookup_country(sample_ip)
    update!(country: geo_country) if geo_country.present?
  rescue => e
    Rails.logger.error "Failed to lookup geo location for network range #{cidr}: #{e.message}"
  end

  # Class methods for network operations
  def self.contains_ip(ip_string)
    where("network >>= ?", ip_string)
       .order("masklen(network) DESC") # Most specific first
  end

  def self.overlapping(range_cidr)
    where("network && ?", range_cidr)
  end

  def self.find_or_create_by_cidr(cidr, user: nil, source: nil, reason: nil)
    find_or_create_by(network: cidr) do |range|
      range.user = user
      range.source = source || 'user_created'
      range.creation_reason = reason
    end
  end

  def self.import_from_cidr(cidr, **attributes)
    find_or_create_by(network: cidr) do |range|
      range.assign_attributes(attributes)
    end
  end

  # Convenience methods for JSON fields
  def abuser_scores_hash
    abuser_scores ? JSON.parse(abuser_scores) : {}
  rescue JSON::ParserError
    {}
  end

  def abuser_scores_hash=(hash)
    self.abuser_scores = hash.to_json
  end

  def additional_data_hash
    additional_data ? JSON.parse(additional_data) : {}
  rescue JSON::ParserError
    {}
  end

  def additional_data_hash=(hash)
    self.additional_data = hash.to_json
  end

  # String representations
  def to_s
    cidr
  end

  def to_param
    cidr.to_s.gsub('/', '_')
  end

  # Analytics methods - events_count is now a counter cache column maintained by database triggers
  # This is much more performant than the previous implementation that did complex network queries
  def events_count
    self[:events_count] || 0
  end

  def recent_events(limit: 100)
    Event.where(ip_address: child_ranges.pluck(:network_address) + [network_address])
         .recent
         .limit(limit)
  end

  def blocking_rules
    rules.where(action: 'deny', enabled: true)
  end

  def active_rules
    rules.enabled.where("expires_at IS NULL OR expires_at > ?", Time.current)
  end

  private

  def set_default_source
    self.source ||= 'api_imported'
  end

  def should_update_children_inheritance?
    saved_change_to_attribute?(:asn) ||
    saved_change_to_attribute?(:company) ||
    saved_change_to_attribute?(:country) ||
    saved_change_to_attribute?(:is_datacenter) ||
    saved_change_to_attribute?(:is_proxy) ||
    saved_change_to_attribute?(:is_vpn)
  end

  def update_children_inheritance!
    # Find child ranges that don't have their own intelligence
    child_without_intelligence = child_ranges.where(
      asn: nil,
      company: nil,
      country: nil,
      is_datacenter: false,
      is_proxy: false,
      is_vpn: false
    )

    child_without_intelligence.find_each do |child|
      Rails.logger.info "Child range #{child.cidr} can now inherit from parent #{cidr}"
      # The inherited_intelligence method will pick up the new parent data
    end
  end
end