/*
 * QQQ - Low-code Application Framework for Engineers.
 * Copyright (C) 2021-2023.  Kingsrook, LLC
 * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
 * contact@kingsrook.com
 * https://github.com/Kingsrook/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

/* eslint-disable @typescript-eslint/no-explicit-any */
import React, {DetailedHTMLProps, forwardRef, useEffect, useRef} from "react";
import "rapidoc";

interface RapiDocProps
   extends DetailedHTMLProps<
      React.HTMLAttributes<HTMLDivElement>,
      HTMLDivElement
   >
{
   // General
   "spec-url": string;
   "update-route"?: boolean;
   "route-prefix"?: string;
   "sort-tags"?: boolean;
   "sort-endpoints-by"?: "path" | "method" | "summary" | "none";
   "heading-text"?: string;
   "goto-path"?: string;
   "fill-request-fields-with-example"?: boolean;
   "persist-auth"?: boolean;
   // UI Colors and Fonts
   theme?: "light" | "dark";
   "bg-color"?: string;
   "text-color"?: string;
   "header-color"?: string;
   "primary-color"?: string;
   "load-fonts"?: boolean;
   "regular-fonts"?: string;
   "mono-fonts"?: string;
   "font-size"?: "default" | "large" | "largest";
   // Navigation
   "use-path-in-nav-bar"?: boolean;
   "nav-bg-color"?: string;
   "nav-text-color"?: string;
   "nav-hover-bg-color"?: string;
   "nav-hover-text-color"?: string;
   "nav-accent-color"?: string;
   "nav-item-spacing"?: "default" | "compact" | "relaxed";
   // UI Layout & Placement
   layout?: "row" | "column";
   "render-style"?: "read" | "view" | "focused";
   "on-nav-tag-click"?: "expand-collapse" | "show-description";
   "schema-style"?: "tree" | "table";
   "schema-expand-level"?: number;
   "schema-description-expanded"?: boolean;
   "schema-hide-read-only"?: "always" | "never" | string;
   "default-schema-tab"?: "model" | "example";
   "response-area-height"?: string;
   // Hide/Show Sections
   "show-info"?: boolean;
   "info-description-headings-in-navbar"?: boolean;
   "show-components"?: boolean;
   "show-header"?: boolean;
   "allow-authentication"?: boolean;
   "allow-spec-url-load"?: boolean;
   "allow-spec-file-load"?: boolean;
   "allow-spec-file-download"?: boolean;
   "allow-search"?: boolean;
   "allow-advanced-search"?: boolean;
   "allow-try"?: boolean;
   "allow-server-selection"?: boolean;
   "allow-schema-description-expand-toggle"?: boolean;
   // API Server & calls
   "server-url"?: string;
   "default-api-server"?: string;
   "api-key-name"?: string;
   "api-key-location"?: "header" | "query";
   "api-key-value"?: string;
   "fetch-credentials"?: "omit" | "same-origin" | "include";
   // Events
   beforeRender?: (spec: any) => void;
   specLoaded?: (spec: any) => void;
   beforeTry?: (request: any) => any;
   afterTry?: (data: any) => any;
   apiServerChange?: (server: any) => any;
}

declare global
{
   interface HTMLElementTagNameMap
   {
      "rapi-doc": HTMLDivElement;
   }

   /* eslint-disable @typescript-eslint/no-namespace */
   namespace JSX
   {
      interface IntrinsicElements
      {
         "rapi-doc": RapiDocProps;
      }
   }
}

export const RapiDocReact = forwardRef<HTMLDivElement, RapiDocProps>(
   (
      {
         beforeRender,
         specLoaded,
         beforeTry,
         afterTry,
         apiServerChange,
         children,
         ...props
      }: RapiDocProps,
      ref
   ) =>
   {
      const localRef = useRef<HTMLDivElement>(null);

      useEffect(() =>
      {
         const rapiDocRef =
            typeof ref === "object" && ref?.current
               ? ref?.current
               : localRef.current;

         const handleBeforeRender = (spec: any) =>
         {
            beforeRender && beforeRender(spec);
         };

         const handleSpecLoaded = (spec: any) =>
         {
            specLoaded && specLoaded(spec);
         };

         const handleBeforeTry = (request: any) =>
         {
            beforeTry && beforeTry(request);
         };

         const handleAfterTry = (data: any) =>
         {
            afterTry && afterTry(data);
         };

         const handleApiServerChange = (server: any) =>
         {
            apiServerChange && apiServerChange(server);
         };

         console.log("rapiDocRef", rapiDocRef);
         if (rapiDocRef)
         {
            beforeRender &&
            rapiDocRef.addEventListener("before-render", handleBeforeRender);
            specLoaded &&
            rapiDocRef.addEventListener("spec-loaded", handleSpecLoaded);
            beforeTry && rapiDocRef.addEventListener("before-try", handleBeforeTry);
            afterTry && rapiDocRef.addEventListener("after-try", handleAfterTry);
            apiServerChange &&
            rapiDocRef.addEventListener(
               "api-server-change",
               handleApiServerChange
            );
         }
         return () =>
         {
            if (rapiDocRef)
            {
               beforeRender &&
               rapiDocRef.removeEventListener("before-render", handleBeforeRender);
               specLoaded &&
               rapiDocRef.removeEventListener("spec-loaded", handleSpecLoaded);
               beforeTry &&
               rapiDocRef.removeEventListener("before-try", handleBeforeTry);
               afterTry &&
               rapiDocRef.removeEventListener("after-try", handleAfterTry);
               apiServerChange &&
               rapiDocRef.removeEventListener(
                  "api-server-change",
                  handleApiServerChange
               );
            }
         };
      }, [
         ref,
         localRef,
         specLoaded,
         beforeRender,
         beforeTry,
         afterTry,
         apiServerChange,
      ]);

      return (
         <rapi-doc {...props} ref={ref || localRef}>
            {children}
         </rapi-doc>
      );
   }
);

export default RapiDocReact;