class Api::V1::LocationNetworksController < Api::V1::ApiController
  before_filter :user_authentication_for_api
  before_filter :set_location_network, only: [:show, :get_network_devices, :update, :destroy, :get_network_topology_clients]

  api :GET, "/v1/location_networks/list_networks", "List Networks"
  # param :access_token, String, :desc => "User Access Token", :required => true
  param :network_name, String, :desc => "Network name"


  def list_networks
    if params[:ui] == "1"
      per_page = params[:per_page].to_i > 0 ? params[:per_page].to_i : 10
      sort_column = (params[:sort_column].presence || "network_name").to_s
      sort_direction = params[:sort_direction].presence || "asc"
      page = params[:page].to_i
      page = 1 if page < 1
      org_id, user_id = current_user.organisation_id, current_user.id
      
      network_name = params[:network_name]
      network_id = params[:network_id]
      network_template = params[:network_template]
      selector_mode = params[:selector].present?
      
      # Normalize search term
      search_term =
        if params[:search].is_a?(Hash)
          params[:search][:value].to_s.strip
        elsif params[:search].is_a?(String)
          params[:search].strip
        else
          ""
        end
      
      # Decode search_after if passed explicitly
      search_after = params[:search_after].present? ? JSON.parse(params[:search_after]) : nil
      
      # Try to get search_after from cache if page > 1 and search_after not passed explicitly
      if search_after.nil? && page > 1
        cached_token_key = "network_search:user_#{user_id}:page_#{page - 1}"
        search_after = Rails.cache.read(cached_token_key)
      end
      
      # Get permitted networks for user
      permitted_network_ids = nil
      unless current_user.is_user_allowed_all_networks?
        permitted_network_ids = current_user.get_all_permitted_networks_for_non_admin.pluck(:id)
      end

      @total_count = nil
      total_cache_key = "network_search_total:user_#{user_id}"

      # Get total count only if no search_after (for deep pagination skip count)
      if search_after.blank?
        @total_count = LocationNetwork.count_search_results(
          search_term,
          org_id,
          permitted_network_ids,
          network_template,
          network_id,
          network_name,
          selector_mode
        )
        Rails.cache.write(total_cache_key, @total_count, expires_in: 30.minutes)
      else
        @total_count = Rails.cache.read(total_cache_key) || "unknown"
      end
      
      # Execute search with search_after
      search_response = LocationNetwork.search_networks(
        search_term,
        sort_column,
        sort_direction,
        org_id,
        per_page,
        selector_mode,
        network_template,
        permitted_network_ids,
        network_id,
        network_name,
        search_after
      )
      @all_networks = search_response.results
      
      # Extract meta info
      total_count = @total_count || "unknown"
      has_more = search_response.has_more? rescue false
      next_search_after = search_response.next_search_after rescue nil
      
      # Cache next_search_after for next page (page+1)
      if has_more && next_search_after.present?
        cache_key = "network_search:org_#{org_id}:page_#{page}"
        Rails.cache.write(cache_key, next_search_after, expires_in: 30.minutes)
      end
      
      # Generate a page token (UUID) only if there is a next page
      next_page_token = has_more ? SecureRandom.uuid : nil
      
      # Only process JSON requests - early return for HTML
      return unless request.format.json?
      
      # Pagination metadata
      total_pages = total_count == "unknown" ? nil : (total_count.to_f / per_page).ceil
      pagination_meta = {
        current: page,
        previous: page > 1 ? page - 1 : nil,
        next: has_more ? page + 1 : nil,
        pages: total_pages,
        total: total_count
      }
      
      # Shared meta block
      base_meta = {
        page_token: params[:page_token],
        next_page_token: next_page_token,
        next_search_after: has_more ? next_search_after : nil,
        per_page: per_page,
        has_more: has_more,
        count: @all_networks.size
      }.merge(pagination_meta)
      
      # Build response based on mode
      response_data = if selector_mode
        {
          data: {
            networks: @all_networks.map { |n| { id: n.id.to_i, network_name: n.network_name } }
          },
          meta: base_meta,
          status: 200
        }
      elsif params[:draw].present?
        {
          draw: params[:draw].to_i,
          recordsTotal: total_count == "unknown" ? @all_networks.size : total_count,
          recordsFiltered: total_count == "unknown" ? @all_networks.size : total_count,
          data: @all_networks.map { |n| {
            id: n.id.to_i,
            network_name: n.network_name,
            timezone_with_offset: n.tz_with_offset,
            devices_count: n.router_inventories_count,
            tag_list: n.tags,
            external_guid: n.external_guid
          }},
          meta: base_meta
        }
      else
        {
          data: {
            networks: @all_networks.map { |n| {
              id: n.id.to_i,
              network_name: n.network_name,
              cloned_network_id: n.cloned_network_id,
              network_template: n.network_template,
              template_adherence: n.template_adherence,
              enable_policy: n.enable_policy,
              tag_list: n.tags,
              timezone: n.tz_with_offset,
              devices_count: n.router_inventories_count,
              external_guid: n.external_guid
            }}
          },
          meta: base_meta,
          status: 200
        }
      end
      render json: response_data  
    else
      network_name = params[:network_name]
      network_id = params[:network_id]
      @all_networks = @user.location_networks.page(params[:page] || 1).per(params[:per_page] || 100)

      current, total, per_page = @all_networks.current_page, @all_networks.num_pages, @all_networks.limit_value

      if network_name
        network = @user.organisation.location_networks.where(:network_name => network_name).first
      else
        network = @user.organisation.location_networks.where(:id => network_id).first
      end

      if network_name.present? || network_id.present?
        if network.present?
          render json: { data: network.json_build, status: 200 }
        else
          render json: { msg: "Requested Network not found check your Network", error_code: 1001 }, status: 404
        end
      else
        render json: {
          data: {
            networks: @all_networks.map { |n| n.json_build },
            meta: {
              current: current,
              previous: (current > 1 ? (current - 1) : nil),
              next: (current == total ? nil : (current + 1)),
              per_page: per_page,
              pages: total,
              count: @all_networks.total_count
            }
          },
          status: 200
        }
      end
    end
  end

  

  api :GET, "/v1/location_networks/list_network_timezone", "List Network Timezone"
  # param :access_token, String, :desc => "User Access Token", :required => true

  def list_network_timezone
    timezone = ActiveSupport::TimeZone.all.map(&:name).sort
    render json: { data: { timezone: timezone }, status: 200 }
  end

  api :POST, "/v1/location_networks", "Create Network"
  # param :access_token, String, :desc => "User Access Token", :required => true
  param :location_networks, Hash, :desc => "Param description for all fields" do  
    # param :mac_id, String, :desc => "Device Macid", :required => true
    # param :network_configuration, String, :desc => "Configuration to the network default or clone", :required => true
    # param :cloned_network_name, String, :desc => "Existing network name", :required => true
    # param :cloned_network_id, Integer, :desc => "Existing network name", :required => true
    param :network_name, String, :desc => "New network name which you are going to create", :required => true
    param :timezone, String, :desc => "Network you are going to create to which area", :required => true
  end

  def create
    macids = params[:location_network] ? router_inventory_params[:mac_id] : ""
    exist_network_name_params = params[:location_network][:cloned_network_name]
    exist_network_id_params = params[:location_network][:cloned_network_id]
    if exist_network_name_params
      existing_network_name = @user.organisation.location_networks.where(network_name: exist_network_name_params).first
    else
      existing_network_name = @user.organisation.location_networks.where(id: exist_network_id_params).first
    end
    invalid_mac_ids = []
    unless macids.blank?
      inventory = macids.split(',')
      inventory.each do |router|
        status, message = RouterInventory.validate_router(router,@user.organisation)
        invalid_mac_ids << router if status == false
      end
    end
    if !invalid_mac_ids.present?
      if params[:location_network][:network_configuration] == "clone" && existing_network_name.present?
        params[:location_network][:cloned_network_id] = existing_network_name.id
        params[:location_network][:network_configuration] = "clone"
        @network = @user.location_networks.build(location_network_params)
      elsif (params[:location_network][:network_configuration] == "clone") && (!exist_network_name_params.present? && !exist_network_id_params.present?)
        return render json: { msg: "Configuration is clone but the clone network name and clone network id is not present", error_code: 1002}, status: 422
      elsif params[:location_network][:network_configuration] == "clone" && !existing_network_name.present?
        return render json: { msg: "Requested clone network is not found please check and try again.", error_code: 1003}, status: 422
      else
        @network = @user.location_networks.build(location_network_params)
      end
      @network.organisation_id = @user.organisation.id
      if @network.save
        render json: { msg: "Successfully created new Network", data: @network.json_build(true), status: 200 }
        inventory = macids
        unless inventory.blank?
          inventory = inventory.split(',')
          inventory.each do |router|
            RouterInventory.router_activation([router,@network.network_name],@user,false)
          end
        end
      else
        render json: { msg: "Oops! there was some problem in creating Network", errors: "#{@network.errors.full_messages.join(",")}", error_code: 1004}, status: 422
      end
    elsif @user.organisation.router_inventories.count.to_i >= @user.organisation.no_of_aps.to_i
      render json: { msg: "You have reached the maximum limit #{@user.organisation.no_of_aps} to add inventories", error_code: 1005}, status: 422 
    else
      render json: { msg: "Following macid #{invalid_mac_ids.join(",")} are invalid or already provisioned. Please check your macids", error_code: 1006 }, status: 422
    end
  end

  def destroy
    unless @location_network.blank?
      unless @location_network.router_inventories.present?
        if @location_network.destroy
          render json: { msg: "Successfully deleted the Network ( #{@location_network.network_name} )", data: {id: @location_network.id, network_name: @location_network.network_name, timezone: @location_network.timezone}, status: 200}
        else
          render json: { msg: "Oops! there was some problem in deleting network #{@location_network.errors.full_messages}", error_code: 1007}, status: 422
        end
      else
        render json: { msg: "Oops! cannot delete network, Requested network has devices associated with it", data: @location_network.json_build, error_code: 1008}, status: 422
      end
    else
      render json: { msg: "Requested Network is not found", error_code: 1001}, status: 404
    end
  end

  def update
    if @location_network.present?
      if @location_network.update_attributes(update_location_network_params(@location_network))
        render json: { msg: "Successfully updated the network", data: @location_network.json_build, status: 200 }
      else
        render json: { msg: "Oops! there was some problem in updating network", errors: "#{@location_network.errors.full_messages.join(",")}", error_code: 1010}, status: 422
      end
    else
      render json: { msg: "Requested Network not found. Please check your Network", error_code: 1001}, status: 404
    end
  end

  def show
    render json: {data: {location_network: @location_network.json_build}, status: 200}, status: 200
  end

  def get_network_devices
    render json: {data: {devices: @location_network.router_inventories.map(&:json_build)}, status: 200}, status: 200
  end

  def location_network_params
    params.require(:location_network).permit(:id, :network_name, :timezone,:cloned_network_id,:network_configuration, :external_guid, :tag_list => [])
  end

  def update_location_network_params(network)
    if network.network_template && network.enable_policy
      params.require(:location_network).permit(:network_name, template_policy_attributes: [unlock_controls: [ssid_configuration: [unlock: []]]])
    else
      params.require(:location_network).permit(:network_name, :timezone, :presence, :presence_url, :vendor_type, :network_template, :template_adherence, :enable_policy, :external_guid, :template_policy_attributes => [unlock_controls: [ssid_configuration: [unlock: []]]], :tag_list => [])
    end
  end


  def router_inventory_params
    params.require(:location_network).permit(:mac_id)
  end



  # api :POST, "/v1/location_networks/:network_id/config_rollback", "Rollback Network Configuration"
  # param :access_token, String, desc: "User Access Token"
  # param :network_id, :number, desc: "Network ID"
  #param :configuration, Hash, :desc => "configurations for the network like ssid_configuration etc." 
  
  def config_rollback
    network_id = params[:network_id]
    network_template_id = params[:network_template_id]
    location_network = @user.organisation.location_networks.where(id: network_id).last
    configuration = params[:configuration] || {}
    exclusions = params[:exclusions] || []
  
    if location_network
      has_switch = location_network.has_switch?
      begin
        validate_exclusions!(exclusions)
        location_network.clear_and_clone_configs(configuration, network_template_id, has_switch, exclusions)
        render json: { message: "Configuration rollback completed for Network: #{location_network.network_name}" }
      rescue ArgumentError => e
        # Catch ArgumentError and return a 400 Bad Request with the error message
        render json: { error: e.message }, status: :bad_request
      rescue ActiveRecord::RecordNotFound => e
        render json: { message: e.message }, status: :not_found
      end
    else
      render json: { message: "Network not found" }, status: :not_found
    end
  end

  def get_network_topology_clients
    input_data = @location_network.get_client_list

    output_data = LocationNetwork.transform_network(input_data)
    render json: { data:{clients: output_data}}
  end


  private

  def set_location_network
    @location_network = @user.organisation.location_networks.where(id: params[:id]).last
    if @location_network.blank?
      render json: { msg: "Requested location network is not found.", error_code: 1001}, status: 404
    end
  end
  

  def validate_exclusions!(exclusions)
    allowed_configs = ["ssid_configuration", "wired_configuration", "switch_configuration", "alerts", "loggings", "v_lans"]
    invalid_items = exclusions - allowed_configs
    
    unless invalid_items.empty?
      raise ActiveRecord::RecordNotFound, "Invalid exclusions: #{invalid_items.join(', ')}"
    end
  end
  
end
