namespace Motus.Abstractions;

/// <summary>
/// Represents a single browser page (tab). Provides methods for navigation, interaction, evaluation, and more.
/// </summary>
public interface IPage : IAsyncDisposable
{
    // --- Properties ---

    /// <summary>
    /// Gets the browser context that owns this page.
    /// </summary>
    IBrowserContext Context { get; }

    /// <summary>
    /// Gets the main frame of the page.
    /// </summary>
    IFrame MainFrame { get; }

    /// <summary>
    /// Gets all frames in the page, including the main frame.
    /// </summary>
    IReadOnlyList<IFrame> Frames { get; }

    /// <summary>
    /// Gets the current URL of the page.
    /// </summary>
    string Url { get; }

    /// <summary>
    /// Gets the keyboard input handler.
    /// </summary>
    IKeyboard Keyboard { get; }

    /// <summary>
    /// Gets the mouse input handler.
    /// </summary>
    IMouse Mouse { get; }

    /// <summary>
    /// Gets the touchscreen input handler.
    /// </summary>
    ITouchscreen Touchscreen { get; }

    /// <summary>
    /// Gets the video recorder for this page, or null if video recording is not enabled.
    /// </summary>
    IVideo? Video { get; }

    /// <summary>
    /// Gets whether the page has been closed.
    /// </summary>
    bool IsClosed { get; }

    /// <summary>
    /// Gets the viewport size, or null if no viewport is set.
    /// </summary>
    ViewportSize? ViewportSize { get; }

    // --- Events ---

    /// <summary>
    /// Raised when the page closes.
    /// </summary>
    event EventHandler? Close;

    /// <summary>
    /// Raised when a console message is logged.
    /// </summary>
    event EventHandler<ConsoleMessageEventArgs>? Console;

    /// <summary>
    /// Raised when a JavaScript dialog is opened.
    /// </summary>
    event EventHandler<DialogEventArgs>? Dialog;

    /// <summary>
    /// Raised when a download is started.
    /// </summary>
    event EventHandler<IDownload>? Download;

    /// <summary>
    /// Raised when a file chooser is opened.
    /// </summary>
    event EventHandler<IFileChooser>? FileChooser;

    /// <summary>
    /// Raised when an uncaught exception occurs in the page.
    /// </summary>
    event EventHandler<PageErrorEventArgs>? PageError;

    /// <summary>
    /// Raised when a popup page is opened.
    /// </summary>
    event EventHandler<IPage>? Popup;

    /// <summary>
    /// Raised when a network request is made.
    /// </summary>
    event EventHandler<RequestEventArgs>? Request;

    /// <summary>
    /// Raised when a network request fails.
    /// </summary>
    event EventHandler<RequestEventArgs>? RequestFailed;

    /// <summary>
    /// Raised when a network request finishes.
    /// </summary>
    event EventHandler<RequestEventArgs>? RequestFinished;

    /// <summary>
    /// Raised when a network response is received.
    /// </summary>
    event EventHandler<ResponseEventArgs>? Response;

    // --- Navigation ---

    /// <summary>
    /// Navigates the page to the specified URL.
    /// </summary>
    /// <param name="url">The URL to navigate to.</param>
    /// <param name="options">Navigation options.</param>
    /// <returns>The response of the navigation, or null.</returns>
    Task<IResponse?> GotoAsync(string url, NavigationOptions? options = null);

    /// <summary>
    /// Navigates back in the page history.
    /// </summary>
    /// <param name="options">Navigation options.</param>
    /// <returns>The response of the navigation, or null.</returns>
    Task<IResponse?> GoBackAsync(NavigationOptions? options = null);

    /// <summary>
    /// Navigates forward in the page history.
    /// </summary>
    /// <param name="options">Navigation options.</param>
    /// <returns>The response of the navigation, or null.</returns>
    Task<IResponse?> GoForwardAsync(NavigationOptions? options = null);

    /// <summary>
    /// Reloads the current page.
    /// </summary>
    /// <param name="options">Navigation options.</param>
    /// <returns>The response of the reload, or null.</returns>
    Task<IResponse?> ReloadAsync(NavigationOptions? options = null);

    // --- Content ---

    /// <summary>
    /// Returns the full HTML content of the page.
    /// </summary>
    /// <returns>The HTML content.</returns>
    Task<string> ContentAsync();

    /// <summary>
    /// Sets the HTML content of the page.
    /// </summary>
    /// <param name="html">The HTML content to set.</param>
    /// <param name="options">Navigation options.</param>
    Task SetContentAsync(string html, NavigationOptions? options = null);

    /// <summary>
    /// Returns the title of the page.
    /// </summary>
    /// <returns>The page title.</returns>
    Task<string> TitleAsync();

    // --- Accessibility ---

    /// <summary>
    /// Fetches the page's accessibility tree. Each returned node carries its role,
    /// accessible name, properties, children, and (where available) a backend DOM
    /// node identifier that <see cref="LocatorByBackendNodeId"/> can target.
    /// </summary>
    /// <param name="ct">A token to cancel the operation.</param>
    /// <returns>A snapshot of the accessibility tree.</returns>
    Task<AccessibilitySnapshot> AccessibilitySnapshotAsync(CancellationToken ct = default);

    /// <summary>
    /// Runs the built-in WCAG accessibility rules against the page's accessibility
    /// tree and returns the violations found. Each violation carries a rule id,
    /// severity, message, and, where the offending element is addressable, the
    /// backend DOM node identifier that <see cref="LocatorByBackendNodeId"/> can target.
    /// </summary>
    /// <param name="ct">A token to cancel the operation.</param>
    /// <returns>The audit result, including the violations and pass count.</returns>
    Task<AccessibilityAuditResult> RunAccessibilityAuditAsync(CancellationToken ct = default);

    /// <summary>
    /// Returns the page's performance metrics: Core Web Vitals (LCP, FCP, CLS, INP)
    /// along with TTFB, JS heap size, and DOM node count. The metrics reflect the most
    /// recent navigation, with the latest Core Web Vitals re-read on each call so values
    /// that settle after load are picked up. Returns null when no metrics have been
    /// collected yet.
    /// </summary>
    /// <param name="ct">A token to cancel the operation.</param>
    /// <returns>The current performance metrics, or null if none have been collected.</returns>
    Task<PerformanceMetrics?> GetPerformanceMetricsAsync(CancellationToken ct = default);

    /// <summary>
    /// Begins recording this page's network traffic as an HTTP archive. Requests and
    /// responses that occur after this call are captured until
    /// <see cref="StopHarRecordingAsync"/> writes them out. Calling this while a
    /// recording is already in progress has no effect.
    /// </summary>
    /// <param name="ct">A token to cancel the operation.</param>
    Task StartHarRecordingAsync(CancellationToken ct = default);

    /// <summary>
    /// Stops the recording started by <see cref="StartHarRecordingAsync"/> and writes
    /// the captured traffic to <paramref name="path"/> as an HTTP archive (HAR 1.2)
    /// file. Throws if no recording is in progress.
    /// </summary>
    /// <param name="path">The file path to write the HAR archive to.</param>
    /// <param name="ct">A token to cancel the operation.</param>
    Task StopHarRecordingAsync(string path, CancellationToken ct = default);

    /// <summary>
    /// Begins recording this page to a video file (MJPEG in an AVI container,
    /// captured via the browser's screencast, which paces frames by screen
    /// updates and shows no pointer cursor). Throws if a recording is already
    /// in progress, including one started by the context's RecordVideo option.
    /// </summary>
    /// <param name="path">The output file path; when null, a file is created under the system temp directory.</param>
    /// <param name="size">The capture resolution; when null, the page's viewport size is used.</param>
    /// <param name="ct">A token to cancel the operation.</param>
    /// <returns>The path the video is being written to.</returns>
    Task<string> StartVideoRecordingAsync(string? path = null, ViewportSize? size = null, CancellationToken ct = default);

    /// <summary>
    /// Stops the recording started by <see cref="StartVideoRecordingAsync"/> and
    /// finalizes the video file. Throws if no recording is in progress, or if the
    /// recording was started by the context's RecordVideo option (those span the
    /// page's life and finalize when it closes).
    /// </summary>
    /// <param name="ct">A token to cancel the operation.</param>
    /// <returns>The path of the finalized video file.</returns>
    Task<string> StopVideoRecordingAsync(CancellationToken ct = default);

    // --- Locators ---

    /// <summary>
    /// Creates a locator for the specified selector.
    /// </summary>
    /// <param name="selector">The selector to locate elements.</param>
    /// <param name="options">Locator options.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator Locator(string selector, LocatorOptions? options = null);

    /// <summary>
    /// Creates a locator that targets a single element by its backend DOM node
    /// identifier, as reported on <see cref="AccessibilityNode.BackendDOMNodeId"/>
    /// by <see cref="AccessibilitySnapshotAsync"/>. The element is resolved when an
    /// action is first invoked on the locator; if the node is no longer attached to
    /// the document, the action fails.
    /// </summary>
    /// <param name="backendNodeId">The backend DOM node identifier to target.</param>
    /// <returns>A locator for the matching element.</returns>
    ILocator LocatorByBackendNodeId(long backendNodeId);

    /// <summary>
    /// Creates a locator for the element matching the specified role.
    /// </summary>
    /// <param name="role">The ARIA role to match.</param>
    /// <param name="name">Optional accessible name to filter by.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByRole(string role, string? name = null);

    /// <summary>
    /// Creates a locator for the element matching the specified text.
    /// </summary>
    /// <param name="text">The text to match.</param>
    /// <param name="exact">Whether to match the text exactly.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByText(string text, bool? exact = null);

    /// <summary>
    /// Creates a locator for the element matching the specified label.
    /// </summary>
    /// <param name="text">The label text to match.</param>
    /// <param name="exact">Whether to match the text exactly.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByLabel(string text, bool? exact = null);

    /// <summary>
    /// Creates a locator for the element matching the specified placeholder.
    /// </summary>
    /// <param name="text">The placeholder text to match.</param>
    /// <param name="exact">Whether to match the text exactly.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByPlaceholder(string text, bool? exact = null);

    /// <summary>
    /// Creates a locator for the element matching the specified test ID.
    /// </summary>
    /// <param name="testId">The test ID to match.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByTestId(string testId);

    /// <summary>
    /// Creates a locator for the element matching the specified title.
    /// </summary>
    /// <param name="text">The title text to match.</param>
    /// <param name="exact">Whether to match the text exactly.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByTitle(string text, bool? exact = null);

    /// <summary>
    /// Creates a locator for the element matching the specified alt text.
    /// </summary>
    /// <param name="text">The alt text to match.</param>
    /// <param name="exact">Whether to match the text exactly.</param>
    /// <returns>A locator for the matching elements.</returns>
    ILocator GetByAltText(string text, bool? exact = null);

    // --- Evaluation ---

    /// <summary>
    /// Evaluates a JavaScript expression in the page context.
    /// </summary>
    /// <typeparam name="T">The expected return type.</typeparam>
    /// <param name="expression">The JavaScript expression to evaluate.</param>
    /// <param name="arg">Optional argument to pass to the expression.</param>
    /// <returns>The result of the evaluation.</returns>
    Task<T> EvaluateAsync<T>(string expression, object? arg = null);

    /// <summary>
    /// Evaluates a JavaScript expression and returns a handle to the result.
    /// </summary>
    /// <param name="expression">The JavaScript expression to evaluate.</param>
    /// <param name="arg">Optional argument to pass to the expression.</param>
    /// <returns>A handle to the result.</returns>
    Task<IJSHandle> EvaluateHandleAsync(string expression, object? arg = null);

    // --- Waiting ---

    /// <summary>
    /// Waits for a function to return a truthy value.
    /// </summary>
    /// <typeparam name="T">The expected return type.</typeparam>
    /// <param name="expression">The JavaScript expression to evaluate.</param>
    /// <param name="arg">Optional argument to pass to the expression.</param>
    /// <param name="timeout">Maximum time in milliseconds to wait.</param>
    /// <returns>The return value of the function.</returns>
    Task<T> WaitForFunctionAsync<T>(string expression, object? arg = null, double? timeout = null);

    /// <summary>
    /// Waits for the specified load state.
    /// </summary>
    /// <param name="state">The load state to wait for.</param>
    /// <param name="timeout">Maximum time in milliseconds to wait.</param>
    Task WaitForLoadStateAsync(LoadState? state = null, double? timeout = null);

    /// <summary>
    /// Waits for a matching URL navigation.
    /// </summary>
    /// <param name="urlPattern">The URL pattern to match.</param>
    /// <param name="options">Navigation options.</param>
    Task WaitForURLAsync(string urlPattern, NavigationOptions? options = null);

    /// <summary>
    /// Waits for a network request matching the URL pattern.
    /// </summary>
    /// <param name="urlPattern">The URL pattern to match.</param>
    /// <param name="timeout">Maximum time in milliseconds to wait.</param>
    /// <returns>The matching request.</returns>
    Task<IRequest> WaitForRequestAsync(string urlPattern, double? timeout = null);

    /// <summary>
    /// Waits for a network response matching the URL pattern.
    /// </summary>
    /// <param name="urlPattern">The URL pattern to match.</param>
    /// <param name="timeout">Maximum time in milliseconds to wait.</param>
    /// <returns>The matching response.</returns>
    Task<IResponse> WaitForResponseAsync(string urlPattern, double? timeout = null);

    /// <summary>
    /// Waits for a specified amount of time.
    /// </summary>
    /// <param name="timeout">The time to wait in milliseconds.</param>
    Task WaitForTimeoutAsync(double timeout);

    // --- Screenshots ---

    /// <summary>
    /// Takes a screenshot of the page.
    /// </summary>
    /// <param name="options">Screenshot options.</param>
    /// <returns>The screenshot as a byte array.</returns>
    Task<byte[]> ScreenshotAsync(ScreenshotOptions? options = null);

    // --- Routing ---

    /// <summary>
    /// Registers a route handler for requests matching the URL pattern.
    /// </summary>
    /// <param name="urlPattern">The URL pattern to match.</param>
    /// <param name="handler">The route handler.</param>
    Task RouteAsync(string urlPattern, Func<IRoute, Task> handler);

    /// <summary>
    /// Removes a previously registered route handler.
    /// </summary>
    /// <param name="urlPattern">The URL pattern to unregister.</param>
    /// <param name="handler">The handler to remove. If null, removes all handlers for the pattern.</param>
    Task UnrouteAsync(string urlPattern, Func<IRoute, Task>? handler = null);

    // --- Misc ---

    /// <summary>
    /// Sets the viewport size.
    /// </summary>
    /// <param name="viewportSize">The new viewport size.</param>
    Task SetViewportSizeAsync(ViewportSize viewportSize);

    /// <summary>
    /// Adds a script tag to the page.
    /// </summary>
    /// <param name="url">URL of the script to add.</param>
    /// <param name="content">Inline script content.</param>
    /// <returns>An element handle to the script element.</returns>
    Task<IElementHandle> AddScriptTagAsync(string? url = null, string? content = null);

    /// <summary>
    /// Adds a style tag to the page.
    /// </summary>
    /// <param name="url">URL of the stylesheet to add.</param>
    /// <param name="content">Inline CSS content.</param>
    /// <returns>An element handle to the style element.</returns>
    Task<IElementHandle> AddStyleTagAsync(string? url = null, string? content = null);

    /// <summary>
    /// Exposes a .NET function to be called from JavaScript.
    /// </summary>
    /// <param name="name">The function name to expose in the page.</param>
    /// <param name="callback">The callback to invoke.</param>
    Task ExposeBindingAsync(string name, Func<object?[], Task<object?>> callback);

    /// <summary>
    /// Closes the page.
    /// </summary>
    /// <param name="runBeforeUnload">Whether to run the beforeunload handler.</param>
    Task CloseAsync(bool? runBeforeUnload = null);

    /// <summary>
    /// Brings the page to the front (activates the tab).
    /// </summary>
    Task BringToFrontAsync();

    /// <summary>
    /// Pauses script execution. Useful for debugging.
    /// </summary>
    Task PauseAsync();

    /// <summary>
    /// Generates a PDF of the page. Only supported in headless Chromium.
    /// </summary>
    /// <param name="path">Optional path to save the PDF to.</param>
    /// <returns>The PDF as a byte array.</returns>
    Task<byte[]> PdfAsync(string? path = null);

    /// <summary>
    /// Emulates the given media type or features.
    /// </summary>
    /// <param name="media">The media type (e.g. "screen", "print").</param>
    /// <param name="colorScheme">The color scheme to emulate.</param>
    Task EmulateMediaAsync(string? media = null, ColorScheme? colorScheme = null);
}
