'use client';

/* eslint-disable @typescript-eslint/no-explicit-any */
import { createElement, useEffect, useRef } from 'react';
import type React from 'react';
import { InvalidField } from '@/TailwindPlay/Fields/Invalid.js';
import type { TailwindPlayCommonParams } from '@/TailwindPlay/Fields/types.js';
import { extractHusarData } from '@/TailwindPlay/shared/extract.js';
import { isValidHtmlComponent } from '@/TailwindPlay/shared/constants.js';

type ScriptKind = 'classic' | 'module';

const externalScriptLoads = new WeakMap<Document, Map<string, Promise<HTMLScriptElement>>>();
const unsuitableContainerTags = new Set(['img', 'input', 'option', 'script', 'select', 'textarea']);

const getScriptKind = (script: HTMLScriptElement): ScriptKind =>
  script.type.trim().toLowerCase() === 'module' ? 'module' : 'classic';

const copyScript = (source: HTMLScriptElement, target: HTMLScriptElement) => {
  Array.from(source.attributes).forEach(({ name, value }) => target.setAttribute(name, value));
  target.text = source.textContent || '';
};

const loadExternalScript = (source: HTMLScriptElement, ownerDocument: Document) => {
  let sourceUrl: string;
  try {
    sourceUrl = new URL(source.getAttribute('src') || '', ownerDocument.baseURI).href;
  } catch {
    return Promise.reject(new Error(`Invalid external script URL: ${source.getAttribute('src') || ''}`));
  }
  const kind = getScriptKind(source);
  const registryKey = `${kind}:${sourceUrl}`;
  let registry = externalScriptLoads.get(ownerDocument);

  if (!registry) {
    registry = new Map();
    externalScriptLoads.set(ownerDocument, registry);
  }

  const registeredLoad = registry.get(registryKey);
  if (registeredLoad) return registeredLoad;

  const existingScript = Array.from(ownerDocument.scripts).find(
    (script) => script.src === sourceUrl && getScriptKind(script) === kind,
  );

  if (existingScript) {
    const existingLoad = Promise.resolve(existingScript);
    registry.set(registryKey, existingLoad);
    return existingLoad;
  }

  const executableScript = ownerDocument.createElement('script');
  copyScript(source, executableScript);
  executableScript.async = source.async;

  let resolveLoad!: (script: HTMLScriptElement) => void;
  let rejectLoad!: (error: Error) => void;
  const load = new Promise<HTMLScriptElement>((resolve, reject) => {
    resolveLoad = resolve;
    rejectLoad = reject;
  });

  executableScript.addEventListener('load', () => resolveLoad(executableScript), { once: true });
  executableScript.addEventListener(
    'error',
    () => {
      if (registry?.get(registryKey) === load) registry.delete(registryKey);
      executableScript.remove();
      rejectLoad(new Error(`Failed to load external script: ${sourceUrl}`));
    },
    { once: true },
  );

  registry.set(registryKey, load);

  const insertionPoint = ownerDocument.head || ownerDocument.documentElement;
  try {
    insertionPoint.appendChild(executableScript);
  } catch (error) {
    if (registry.get(registryKey) === load) registry.delete(registryKey);
    executableScript.remove();
    rejectLoad(error instanceof Error ? error : new Error(`Failed to append external script: ${sourceUrl}`));
  }

  return load;
};

const executeScript = (source: HTMLScriptElement, container: HTMLElement) => {
  if (source.hasAttribute('src')) {
    void loadExternalScript(source, container.ownerDocument).catch(() => undefined);
    return;
  }

  const executableScript = container.ownerDocument.createElement('script');
  copyScript(source, executableScript);
  container.appendChild(executableScript);
};

export const FieldScript: React.FC<TailwindPlayCommonParams> = (props) => {
  const containerRef = useRef<HTMLElement | null>(null);
  const appliedValueRef = useRef<{ container: HTMLElement; value: string } | null>(null);
  const { v, attributes } = extractHusarData<string>(props);

  useEffect(() => {
    const container = containerRef.current;
    if (!container || typeof v !== 'string') return;

    const appliedValue = appliedValueRef.current;
    if (appliedValue?.container === container && appliedValue.value === v) return;

    appliedValueRef.current = { container, value: v };
    container.replaceChildren();

    const trimmedValue = v.trim();
    if (!trimmedValue) return;

    if (!trimmedValue.startsWith('<')) {
      const source = container.ownerDocument.createElement('script');
      source.text = v;
      executeScript(source, container);
      return;
    }

    const template = container.ownerDocument.createElement('template');
    template.innerHTML = v;
    const scripts = Array.from(template.content.querySelectorAll('script'));
    scripts.forEach((script) => script.remove());

    container.appendChild(template.content);
    scripts.forEach((script) => executeScript(script, container));
  }, [v]);

  if (typeof v !== 'string' && v !== null && typeof v !== 'undefined') return <InvalidField />;

  const configuredTag = isValidHtmlComponent(attributes);
  const tag = unsuitableContainerTags.has(configuredTag) ? 'div' : configuredTag;
  const containerAttributes = { ...attributes };
  delete containerAttributes.children;
  delete containerAttributes.component;
  delete containerAttributes.dangerouslySetInnerHTML;

  return createElement(tag, {
    ...containerAttributes,
    ref: containerRef,
  });
};
