# frozen_string_literal: true

# GraphqlController handles GraphQL queries and mutations,
# including authentication and error handling.
class GraphqlController < ApplicationController
  before_action :authenticate_user!, unless: -> { allowed_action? }

  def execute
    variables = prepare_variables(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    token = request.headers['Authorization']&.split&.last

    context = {
      current_user: set_current_user,
      jwt_token: token,
      jwt_payload: decode_jwt_payload(token)
    }

    result = <%= projectName.camelize %>Schema.execute(query, variables:, context:,
                                        operation_name:)
    # binding.break
    render json: result
  rescue StandardError => e
    raise e unless Rails.env.development?

    handle_error_in_development(e)
  end

  private

  def authenticate_user!
    token = request.headers['Authorization']&.split&.last
    return render json: error_response('Token is missing', 401) unless token

    jwt_payload = decode_jwt_payload(token)
    return render json: error_response('Invalid token', 401) unless jwt_payload

    user = User.find_by(id: jwt_payload['sub'])
    render json: error_response('User not found', 401) unless user
  end

  # Define actions that do not require authentication
  def allowed_action?
    allowed_actions = %w[CreateUser SignUp SignIn OtpRequest PasswordReset CreateLog CreateCompany]
    operation_name = params[:operationName]
    allowed_actions.any? { |action| operation_name == action }
  end

  # Return a structured error response
  def error_response(error, status)
    {
      'data' => nil,
      'errors' => [error],
      'message' => error,
      'httpStatus' => status
    }
  end

  def set_current_user
    token = request.headers['Authorization']&.split&.last
    return nil unless token

    begin
      jwt_payload = decode_jwt_payload(token)
      return nil unless jwt_payload

      User.find(jwt_payload['sub'])
    rescue JWT::DecodeError, Mongoid::Errors::DocumentNotFound
      nil
    end
  end

  def decode_jwt_payload(token = nil)
    return nil if token.nil?

    begin
      @jwt_secret ||= ConfigHelper.jwt_secret_key
      JWT.decode(token, @jwt_secret, true, algorithm: 'HS256').first
    rescue JWT::DecodeError => e
      Rails.logger.error "JWT decode error: #{e.message}"
      nil
    end
  end

  def prepare_variables(variables_param)
    case variables_param
    when String
      if variables_param.present?
        JSON.parse(variables_param) || {}
      else
        {}
      end
    when Hash
      variables_param
    when ActionController::Parameters
      variables_param.to_unsafe_hash
    when nil
      {}
    else
      raise ArgumentError, "Unexpected parameter: #{variables_param}"
    end
  end

  def handle_error_in_development(e)
    logger.error e.message
    logger.error e.backtrace.join("\n")
    render json: { error: { message: e.message, backtrace: e.backtrace }, data: {} },
           status: :internal_server_error
  end
end 