# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2023-2026, by Samuel Williams.

require_relative "request_helper"
require_relative "element"

require_relative "scope"

module Async
	module WebDriver
		# A session represents a single browser session, potentially with multiple windows. It is the primary interface for interacting with a browser.
		#
		# ``` ruby
		# begin
		# 	bridge = Async::WebDriver::Bridge::Pool.start(Async::WebDriver::Bridge::Chrome.new)
		# 	session = bridge.session
		# 	session.navigate_to("https://google.com")
		# 	# ...
		# ensure
		# 	bridge&.close
		# end
		# ```
		class Session
			# Open a new session.
			# @parameter endpoint [Async::HTTP::Endpoint] The endpoint to connect to.
			# @yields {|session| ...} The session will be closed automatically if you provide a block.
			# 	@parameter session [Session] The session.
			# @returns [Session] The session if no block is given.
			def self.open(endpoint, *arguments, **options)
				client = self.new(
					Async::HTTP::Client.open(endpoint),
					*arguments,
					**options
				)
				
				return client unless block_given?
				
				begin
					yield client
				ensure
					client.close
				end
			end
			
			# Initialize the session.
			# @parameter delegate [Protocol::HTTP::Middleware] The underlying HTTP client (or wrapper).
			# @parameter id [String] The session identifier.
			# @parameter capabilities [Hash] The capabilities of the session.
			def initialize(delegate, id, capabilities, **options)
				@delegate = delegate
				@id = id
				@capabilities = capabilities
				
				@options = options
			end
			
			# @returns [String] A concise representation of the session.
			def inspect
				"\#<#{self.class} id=#{@id.inspect}>"
			end
			
			# @attribute [Protocol::HTTP::Middleware] The underlying HTTP client (or wrapper).
			attr :delegate
			
			# @attribute [String] The session identifier.
			attr :id
			
			# @attribute [Hash] The capabilities of the session.
			attr :capabilities
			
			# The path used for making requests to the web driver bridge.
			# @parameter path [String | Nil] The path to append to the request path.
			# @returns [String] The path used for making requests to the web driver bridge.
			def request_path(path = nil)
				if path
					"/session/#{@id}/#{path}"
				else
					"/session/#{@id}"
				end
			end
			
			include RequestHelper
			
			# Close the session.
			def close
				if @delegate
					self.delete
					@delegate = nil
				end
			end
			
			# @returns [Session] The session.
			def session
				self
			end
			
			# @returns [Session] The current scope.
			def current_scope
				self
			end
			
			# Execute a script in the current document.
			# @parameter script [String] The script to execute.
			# @parameter arguments [Array] The arguments to pass to the script.
			# @returns [Object] The result of the script.
			def execute(script, *arguments)
				post("execute/sync", {script: script, args: arguments})
			end
			
			# Execute a script in the current document asynchronously.
			# @parameter script [String] The script to execute.
			# @parameter arguments [Array] The arguments to pass to the script.
			# @returns [Object] The result of the script.
			def execute_async(script, *arguments)
				post("execute/async", {script: script, args: arguments})
			end
			
			include Scope::Alerts
			include Scope::Cookies
			include Scope::Document
			include Scope::Elements
			include Scope::Fields
			include Scope::Frames
			include Scope::Navigation
			include Scope::Printing
			include Scope::ScreenCapture
			include Scope::Timeouts
			include Scope::Window
			
			# Reset the session to a clean state.
			def reset!
				# Go to a blank page (in theory this should also invalidate any Element instances):
				self.navigate_to("about:blank")
				
				# Clear cookies and local storage:
				self.delete_all_cookies
				
				# This does not work consistently:
				# self.execute("localStorage.clear();")
				
				# Detach the session instance from the underlying HTTP client:
				@delegate = nil
			end
		end
	end
end
