# frozen_string_literal: true

class RulesController < ApplicationController
  # Follow proper before_action order:
  # 1. Authentication/Authorization
  # All actions require authentication

  # 2. Resource loading
  before_action :set_rule, only: [:show, :edit, :update, :disable, :enable]

  # GET /rules
  def index
    @pagy, @rules = pagy(policy_scope(Rule).includes(:user, :network_range).order(created_at: :desc))
    @waf_rule_types = Rule.waf_rule_types
    @waf_actions = Rule.waf_actions
  end

  # GET /rules/new
  def new
    authorize Rule
    @rule = Rule.new

    # Pre-fill from URL parameters
    if params[:network_range_id].present?
      network_range = NetworkRange.find_by(id: params[:network_range_id])
      @rule.network_range = network_range if network_range
    end

    if params[:cidr].present?
      @rule.waf_rule_type = 'network'
    end

    @waf_rule_types = Rule.waf_rule_types
    @waf_actions = Rule.waf_actions
  end

  # POST /rules
  def create
    authorize Rule
    @rule = Rule.new(rule_params)
    @rule.user = Current.user
    @waf_rule_types = Rule.waf_rule_types
    @waf_actions = Rule.waf_actions

    # Process additional form data for quick create
    process_quick_create_parameters

    # Handle network range creation if CIDR is provided
    if params[:cidr].present? && @rule.network_rule?
      network_range = NetworkRange.find_or_create_by(cidr: params[:cidr]) do |range|
        range.user = Current.user
        range.source = 'manual'
        range.creation_reason = "Created for rule ##{@rule.id}"
      end
      @rule.network_range = network_range
    end

    # Calculate priority automatically based on rule type
    calculate_rule_priority

    if @rule.save
      # For quick create from NetworkRange page, redirect back to network range
      if params[:rule][:network_range_id].present? && request.referer&.include?('/network_ranges/')
        network_range = NetworkRange.find(params[:rule][:network_range_id])
        redirect_to network_range, notice: 'Rule was successfully created.'
      else
        redirect_to @rule, notice: 'Rule was successfully created.'
      end
    else
      render :new, status: :unprocessable_entity
    end
  end

  # GET /rules/:id
  def show
    authorize @rule
  end

  # GET /rules/:id/edit
  def edit
    authorize @rule
    @waf_rule_types = Rule.waf_rule_types
    @waf_actions = Rule.waf_actions
  end

  # PATCH/PUT /rules/:id
  def update
    authorize @rule

    # Preserve original attributes in case validation fails
    original_attributes = @rule.attributes.dup
    original_network_range_id = @rule.network_range_id

    if @rule.update(rule_params)
      redirect_to @rule, notice: 'Rule was successfully updated.'
    else
      # Restore original attributes to preserve form state
      # This prevents network range dropdown from resetting
      @rule.attributes = original_attributes
      @rule.network_range_id = original_network_range_id

      render :edit, status: :unprocessable_entity
    end
  end

  # POST /rules/:id/disable
  def disable
    authorize @rule, :disable?
    reason = params[:reason] || "Disabled manually"
    @rule.disable!(reason: reason)
    redirect_to @rule, notice: 'Rule was successfully disabled.'
  end

  # POST /rules/:id/enable
  def enable
    authorize @rule, :enable?
    @rule.enable!
    redirect_to @rule, notice: 'Rule was successfully enabled.'
  end

  private

  def set_rule
    @rule = Rule.find(params[:id])
  end

  def rule_params
  permitted = [
    :waf_rule_type,
    :waf_action,
    :metadata,
    :expires_at,
    :enabled,
    :source,
    :network_range_id
  ]

  # Only include conditions for non-network rules
  if params[:rule][:waf_rule_type] != 'network'
    permitted << :conditions
  end

  params.require(:rule).permit(permitted)
end

def calculate_rule_priority
  return unless @rule

  case @rule.waf_rule_type
  when 'network'
    # For network rules, priority based on prefix specificity
    if @rule.network_range
      prefix = @rule.network_range.prefix_length
      @rule.priority = case prefix
                      when 32 then 200  # /32 single IP
                      when 31 then 190
                      when 30 then 180
                      when 29 then 170
                      when 28 then 160
                      when 27 then 150
                      when 26 then 140
                      when 25 then 130
                      when 24 then 120
                      when 23 then 110
                      when 22 then 100
                      when 21 then 90
                      when 20 then 80
                      when 19 then 70
                      when 18 then 60
                      when 17 then 50
                      when 16 then 40
                      when 15 then 30
                      when 14 then 20
                      when 13 then 10
                      else 0
                      end
    else
      @rule.priority = 100 # Default for network rules without range
    end
  when 'path_pattern'
    @rule.priority = 85
  when 'rate_limit'
    @rule.priority = 70
  else
    @rule.priority = 50 # Default priority
  end
end

def process_quick_create_parameters
  return unless @rule

  # Handle path pattern parameters
  if @rule.path_pattern_rule? && params[:path_pattern].present? && params[:match_type].present?
    begin
      pattern = params[:path_pattern]
      match_type = params[:match_type]

      # Parse pattern to segments
      segments = pattern.split('/').reject(&:blank?).map(&:downcase)

      # Find or create PathSegment entries
      segment_ids = segments.map do |seg|
        PathSegment.find_or_create_segment(seg).id
      end

      # Set conditions with segment IDs and match type
      @rule.conditions = {
        segment_ids: segment_ids,
        match_type: match_type,
        original_pattern: pattern
      }

      # Add to metadata for display
      @rule.metadata ||= {}
      @rule.metadata.merge!({
        segments: segments,
        pattern_display: "/" + segments.join("/")
      })
    rescue => e
      @rule.errors.add(:base, "Failed to create path pattern: #{e.message}")
    end
  end

  # Handle rate limiting parameters
  if @rule.rate_limit_rule? && params[:rate_limit].present? && params[:rate_window].present?
    rate_limit_data = {
      limit: params[:rate_limit].to_i,
      window_seconds: params[:rate_window].to_i,
      scope: 'per_ip'
    }

    # Update conditions with rate limit data
    @rule.conditions ||= {}
    @rule.conditions.merge!(rate_limit_data)
  end

  # Handle redirect URL
  if @rule.redirect_action? && params[:redirect_url].present?
    @rule.metadata ||= {}
    if @rule.metadata.is_a?(String)
      begin
        @rule.metadata = JSON.parse(@rule.metadata)
      rescue JSON::ParserError
        @rule.metadata = {}
      end
    end
    @rule.metadata.merge!({
      redirect_url: params[:redirect_url],
      redirect_status: 302
    })
  end

  # Parse metadata if it's a string that looks like JSON
  if @rule.metadata.is_a?(String) && @rule.metadata.starts_with?('{')
    begin
      @rule.metadata = JSON.parse(@rule.metadata)
    rescue JSON::ParserError
      # Keep as string if not valid JSON
    end
  end

  # Handle expires_at parsing for text input
  if params.dig(:rule, :expires_at).present?
    expires_at_str = params[:rule][:expires_at].strip
    if expires_at_str.present?
      begin
        # Try to parse various datetime formats
        @rule.expires_at = DateTime.parse(expires_at_str)
      rescue ArgumentError
        # Try specific format
        begin
          @rule.expires_at = DateTime.strptime(expires_at_str, '%Y-%m-%d %H:%M')
        rescue ArgumentError
          @rule.errors.add(:expires_at, 'must be in format YYYY-MM-DD HH:MM')
        end
      end
    end
  end

  # Add reason to metadata if provided
  if params.dig(:rule, :metadata).present?
    if @rule.metadata.is_a?(Hash)
      @rule.metadata['reason'] = params[:rule][:metadata]
    else
      @rule.metadata = { 'reason' => params[:rule][:metadata] }
    end
  end
end

  private

  def set_rule
    @rule = Rule.find(params[:id])
  end

  def rule_params
    permitted = [
      :waf_rule_type,
      :waf_action,
      :metadata,
      :expires_at,
      :enabled,
      :source,
      :network_range_id
    ]

    # Only include conditions for non-network rules
    if params[:rule][:waf_rule_type] != 'network'
      permitted << :conditions
    end

    params.require(:rule).permit(permitted)
  end

  def calculate_rule_priority
    return unless @rule

    case @rule.waf_rule_type
    when 'network'
      # For network rules, priority based on prefix specificity
      if @rule.network_range
        prefix = @rule.network_range.prefix_length
        @rule.priority = case prefix
                        when 32 then 200  # /32 single IP
                        when 31 then 190
                        when 30 then 180
                        when 29 then 170
                        when 28 then 160
                        when 27 then 150
                        when 26 then 140
                        when 25 then 130
                        when 24 then 120
                        when 23 then 110
                        when 22 then 100
                        when 21 then 90
                        when 20 then 80
                        when 19 then 70
                        when 18 then 60
                        when 17 then 50
                        when 16 then 40
                        when 15 then 30
                        when 14 then 20
                        when 13 then 10
                        else 0
                        end
      else
        @rule.priority = 100 # Default for network rules without range
      end
    when 'path_pattern'
      @rule.priority = 85
    when 'rate_limit'
      @rule.priority = 70
    else
      @rule.priority = 50 # Default priority
    end
  end
end