import { UEBridge } from './bridge';
import { resolveBootState, type BrowserBootState } from './boot-state';
import {
  BrowserSupportAdapters,
  listAdapters,
  loadAdapter,
  resolveSupportAssetUrl
} from './registry';

type BrowserSupportAdapter = (typeof BrowserSupportAdapters)[number];

type RuntimeCounters = {
  commandReceiveCount: number;
  shellStateReceiveCount: number;
};

type LogWriter = (Title: string, Message: string) => void;

type RuntimeStatusPanel = {
  panel: HTMLElement;
  refresh: () => void;
};

type StatePanel = {
  panel: HTMLElement;
  setPayload: (Payload: unknown) => void;
};

type UtilitySection = {
  appendLog: LogWriter;
  refreshRuntimeStatus: () => void;
  section: HTMLElement;
  setShellStatePayload: (Payload: unknown) => void;
};

function createElement<K extends keyof HTMLElementTagNameMap>(
  TagName: K,
  ClassName?: string,
  TextContent?: string
): HTMLElementTagNameMap[K]
{
  const Element = document.createElement(TagName);
  if (ClassName)
  {
    Element.className = ClassName;
  }
  if (TextContent)
  {
    Element.textContent = TextContent;
  }
  return Element;
}

function serializePretty(Payload: unknown): string
{
  if (typeof Payload === 'string')
  {
    return Payload;
  }

  try
  {
    return JSON.stringify(Payload, null, 2);
  }
  catch
  {
    return String(Payload);
  }
}

function createSectionHeader(Title: string, Note: string): HTMLElement
{
  const Header = createElement('div', 'section-header');
  Header.append(
    createElement('h2', 'section-title', Title),
    createElement('p', 'section-note', Note)
  );
  return Header;
}

function createHeroSection(AdapterCount: number): HTMLElement
{
  const HeroElement = createElement('section', 'hero');
  const HeroTitle = createElement('h1', undefined, 'HyperTwist Browser Runtime');
  const HeroParagraph = createElement(
    'p',
    undefined,
    'The authoritative shell now lives in Content/Browser/index.html. When the bundled runtime is present the shell upgrades into the full browser stack; when build artifacts have been cleaned, the same shell still remains truthful and runnable through the plain-JS fallback lane.'
  );
  const HeroMetrics = createElement('div', 'hero-grid');

  [
    {
      label: 'Adapters surfaced',
      value: String(AdapterCount)
    },
    {
      label: 'Phase 1 coverage',
      value: '1E, 1G, 1H, 1I, 1J'
    },
    {
      label: 'Bridge route',
      value: 'UE BindUObject + postMessage fallback'
    },
    {
      label: 'Shell authority',
      value: 'index.html + runtime bootstrap'
    }
  ].forEach((Metric) =>
  {
    const MetricElement = createElement('div', 'metric');
    MetricElement.append(
      createElement('strong', undefined, Metric.label),
      createElement('span', undefined, Metric.value)
    );
    HeroMetrics.appendChild(MetricElement);
  });

  HeroElement.append(HeroTitle, HeroParagraph, HeroMetrics);
  return HeroElement;
}

function createLogPanel(): { appendLog: LogWriter; panel: HTMLElement }
{
  const LogPanel = createElement('section', 'panel');
  LogPanel.append(
    createElement('h2', undefined, 'Runtime Log'),
    createElement(
      'p',
      'runtime-note',
      'Compact bridge and adapter events are recorded here for Unreal-side inspection.'
    )
  );

  const LogList = createElement('div', 'log-list');
  LogPanel.appendChild(LogList);

  return {
    panel: LogPanel,
    appendLog(Title: string, Message: string): void
    {
      const Entry = createElement('div', 'log-entry');
      Entry.append(
        createElement('strong', undefined, Title),
        createElement('div', undefined, Message)
      );
      LogList.prepend(Entry);
    }
  };
}

function buildRuntimeStatusSnapshot(
  BootState: BrowserBootState,
  Counters: RuntimeCounters
): Record<string, unknown>
{
  return {
    runtimeStatusId: BootState.runtimeStatusId ?? 'runtime-ready',
    runtimeMode: BootState.runtimeMode ?? 'unknown',
    bootstrapStartedAtUtc: BootState.bootstrapStartedAtUtc ?? 'pending',
    runtimeReadyAtUtc: BootState.runtimeReadyAtUtc ?? 'pending',
    shellAuthority: BootState.shellAuthority ?? 'Content/Browser/index.html',
    adapterCount: BootState.adapterCount ?? BrowserSupportAdapters.length,
    queuedCommandCountAtRuntimeReady: BootState.queuedCommandCountAtRuntimeReady ?? 0,
    queuedShellStateCountAtRuntimeReady: BootState.queuedShellStateCountAtRuntimeReady ?? 0,
    commandReceiveCount: Counters.commandReceiveCount,
    shellStateReceiveCount: Counters.shellStateReceiveCount,
    lastCommandReceivedAtUtc: BootState.lastCommandReceivedAtUtc ?? 'none',
    lastShellStateReceivedAtUtc: BootState.lastShellStateReceivedAtUtc ?? 'none',
    bridgeTrafficStatus: Counters.shellStateReceiveCount > 0
      ? 'shell-state-live'
      : Counters.commandReceiveCount > 0
        ? 'command-only-live'
        : 'awaiting-unreal-traffic',
    fallbackReason: BootState.fallbackReason ?? null
  };
}

function createRuntimeStatusPanel(
  BootState: BrowserBootState,
  Counters: RuntimeCounters,
  AppendLog: LogWriter
): RuntimeStatusPanel
{
  const StatusPanel = createElement('section', 'panel');
  StatusPanel.append(
    createElement('h2', undefined, 'Browser Runtime Status'),
    createElement(
      'p',
      'runtime-note',
      'First-party boot-state metadata now surfaces runtime mode, retained startup queue carryover, and live Unreal bridge traffic inside the authoritative shell.'
    )
  );

  const StatusActions = createElement('div', 'solver-actions');
  const SendStatusButton = createElement('button', undefined, 'Send Runtime Status');
  const StatusData = createElement('div', 'data-block');
  const StatusPre = createElement('pre');
  StatusData.appendChild(StatusPre);
  StatusActions.appendChild(SendStatusButton);
  StatusPanel.append(StatusActions, StatusData);

  const Refresh = (): void =>
  {
    StatusPre.textContent = serializePretty(buildRuntimeStatusSnapshot(BootState, Counters));
  };

  SendStatusButton.addEventListener('click', () =>
  {
    UEBridge.sendState({
      type: 'browser-runtime-status',
      source: 'browser-runtime-shell',
      status: buildRuntimeStatusSnapshot(BootState, Counters)
    });
    AppendLog('Browser Runtime Status', 'Sent the current browser runtime status envelope to Unreal.');
  });

  return {
    panel: StatusPanel,
    refresh: Refresh
  };
}

async function mountAnalyticsDemo(): Promise<void>
{
  const ChartHost = document.getElementById('analytics-demo');
  if (!(ChartHost instanceof HTMLElement))
  {
    return;
  }

  const EChartsModule = await import('echarts');
  const ExistingInstance = EChartsModule.getInstanceByDom(ChartHost);
  const Chart = ExistingInstance || EChartsModule.init(ChartHost, undefined, { renderer: 'canvas' });
  Chart.setOption({
    backgroundColor: 'transparent',
    grid: {
      top: 24,
      right: 16,
      bottom: 24,
      left: 32
    },
    xAxis: {
      type: 'category',
      data: ['Solve 1', 'Solve 2', 'Solve 3', 'Solve 4', 'Solve 5'],
      axisLabel: {
        color: '#afc0d6'
      }
    },
    yAxis: {
      type: 'value',
      axisLabel: {
        color: '#afc0d6',
        formatter: (Value: number) => `${Value}s`
      }
    },
    series: [
      {
        type: 'line',
        data: [24.3, 22.7, 21.8, 20.9, 19.4],
        smooth: true,
        lineStyle: {
          color: '#8ee3ff',
          width: 3
        },
        areaStyle: {
          color: 'rgba(82, 199, 255, 0.18)'
        }
      }
    ]
  });
}

function mountViewerDemo(): void
{
  const ViewerHost = document.getElementById('viewer-demo');
  if (!(ViewerHost instanceof HTMLElement))
  {
    return;
  }

  ViewerHost.innerHTML = '';
  const ModelViewer = document.createElement('model-viewer');
  ModelViewer.setAttribute(
    'src',
    resolveSupportAssetUrl('.external/model-viewer/packages/shared-assets/models/cube.gltf')
  );
  ModelViewer.setAttribute(
    'environment-image',
    resolveSupportAssetUrl('.external/model-viewer/packages/shared-assets/environments/aircraft_workshop_01_1k.hdr')
  );
  ModelViewer.setAttribute('camera-controls', '');
  ModelViewer.setAttribute('auto-rotate', '');
  ModelViewer.setAttribute('shadow-intensity', '1');
  ModelViewer.setAttribute('exposure', '1.1');
  ViewerHost.appendChild(ModelViewer);
}

function createAnalyticsPanel(AppendLog: LogWriter): HTMLElement
{
  const AnalyticsPanel = createElement('section', 'panel');
  AnalyticsPanel.append(
    createElement('h2', undefined, 'Analytics Demo'),
    createElement(
      'p',
      'runtime-note',
      'Loads the ECharts + zrender + echarts-gl lane and renders a small solve-trend chart.'
    )
  );

  const AnalyticsActions = createElement('div', 'solver-actions');
  const LoadAnalyticsButton = createElement('button', undefined, 'Load Analytics Demo');
  LoadAnalyticsButton.addEventListener('click', async () =>
  {
    await loadAdapter('1i-zrender');
    await mountAnalyticsDemo();
    AppendLog('Analytics Demo', 'Loaded the analytics bundle and rendered the demo chart.');
  });
  AnalyticsActions.appendChild(LoadAnalyticsButton);

  const AnalyticsDemo = createElement('div', 'data-block');
  AnalyticsDemo.id = 'analytics-demo';
  AnalyticsDemo.textContent = 'Analytics chart will mount here after the adapter loads.';
  AnalyticsPanel.append(AnalyticsActions, AnalyticsDemo);

  return AnalyticsPanel;
}

function createViewerPanel(AppendLog: LogWriter): HTMLElement
{
  const ViewerPanel = createElement('section', 'panel');
  ViewerPanel.append(
    createElement('h2', undefined, 'Viewer Demo'),
    createElement(
      'p',
      'runtime-note',
      'Loads the model-viewer and Khronos reference-viewer lane, then mounts a bounded local sample asset.'
    )
  );

  const ViewerActions = createElement('div', 'solver-actions');
  const LoadViewerButton = createElement('button', undefined, 'Load Viewer Demo');
  LoadViewerButton.addEventListener('click', async () =>
  {
    await loadAdapter('1j-model-viewer');
    mountViewerDemo();
    AppendLog('Viewer Demo', 'Loaded the model-viewer stack and mounted the local sample asset.');
  });
  ViewerActions.appendChild(LoadViewerButton);

  const ViewerDemo = createElement('div', 'data-block');
  ViewerDemo.id = 'viewer-demo';
  ViewerDemo.textContent = 'Viewer preview will appear here after the adapter loads.';
  ViewerPanel.append(ViewerActions, ViewerDemo);

  return ViewerPanel;
}

function createSolverPanel(AppendLog: LogWriter): HTMLElement
{
  const SolverPanel = createElement('section', 'panel');
  SolverPanel.append(
    createElement('h2', undefined, 'Tentone Fallback Solver'),
    createElement(
      'p',
      'runtime-note',
      'The tentone donor is native/OpenCV, so the browser shell uses a fallback form that sends a solver request envelope back to Unreal instead of faking a browser import.'
    )
  );

  const SolverField = createElement('label', 'field');
  SolverField.appendChild(createElement('span', undefined, 'Classic facelet string'));
  const SolverInput = createElement('textarea') as HTMLTextAreaElement;
  SolverInput.value = 'UUUUUUUUURRRRRRRRRFFFFFFFFFDDDDDDDDDLLLLLLLLLBBBBBBBBB';
  SolverField.appendChild(SolverInput);

  const SolverActions = createElement('div', 'solver-actions');
  const SendSolverButton = createElement('button', undefined, 'Send Solver Request');
  SendSolverButton.addEventListener('click', () =>
  {
    UEBridge.sendState({
      type: 'tentone-native-solver-request',
      source: 'browser-shell-fallback',
      faceletString: SolverInput.value.trim()
    });
    AppendLog('Tentone Fallback Solver', 'Sent a native-sidecar solver request envelope to Unreal.');
  });
  SolverActions.appendChild(SendSolverButton);
  SolverPanel.append(SolverField, SolverActions);

  return SolverPanel;
}

function createStatePanel(): StatePanel
{
  const ShellStatePanel = createElement('section', 'panel');
  ShellStatePanel.append(
    createElement('h2', undefined, 'Browser Shell State'),
    createElement(
      'p',
      'runtime-note',
      'Unreal can push browser-shell state or generic commands into this page through the embedded bridge.'
    )
  );

  const StateData = createElement('div', 'data-block');
  const StatePre = createElement('pre');
  StatePre.textContent = '{\n  "status": "waiting-for-unreal-shell-state"\n}';
  StateData.appendChild(StatePre);
  ShellStatePanel.appendChild(StateData);

  return {
    panel: ShellStatePanel,
    setPayload(Payload: unknown): void
    {
      StatePre.textContent = serializePretty(Payload);
    }
  };
}

function createUtilitySection(
  BootState: BrowserBootState,
  Counters: RuntimeCounters
): UtilitySection
{
  const UtilitySectionElement = createElement('section', 'section');
  UtilitySectionElement.appendChild(
    createSectionHeader(
      'Runtime Utilities',
      'The browser shell can render analytics, preview bundled viewer assets, and fall back into native solver routing without pretending native donors are browser packages or requiring retained build artifacts.'
    )
  );

  const { appendLog, panel: LogPanel } = createLogPanel();
  const { panel: StatusPanel, refresh: RefreshRuntimeStatus } = createRuntimeStatusPanel(
    BootState,
    Counters,
    appendLog
  );
  const StatePanel = createStatePanel();
  const UtilityGrid = createElement('div', 'panel-grid');
  UtilityGrid.append(
    StatusPanel,
    createAnalyticsPanel(appendLog),
    createViewerPanel(appendLog),
    createSolverPanel(appendLog),
    StatePanel.panel,
    LogPanel
  );
  UtilitySectionElement.appendChild(UtilityGrid);

  return {
    appendLog,
    refreshRuntimeStatus: RefreshRuntimeStatus,
    section: UtilitySectionElement,
    setShellStatePayload: StatePanel.setPayload
  };
}

function createAdapterCard(
  Adapter: BrowserSupportAdapter,
  AppendLog: LogWriter
): HTMLElement
{
  const CardElement = createElement('article', 'card');
  const CardHeader = createElement('div', 'card-header');
  const CardTitleBlock = createElement('div');
  const PhaseChip = createElement('span', 'phase-chip', Adapter.phase);
  const CardTitle = createElement('h2', undefined, Adapter.repo);
  const ActivationChip = createElement('span', 'activation-chip', Adapter.activation);
  const StatusChip = createElement('span', 'status-chip', 'idle');
  StatusChip.dataset.status = 'idle';

  CardTitleBlock.append(PhaseChip, CardTitle);
  CardHeader.append(CardTitleBlock, StatusChip);

  const Description = createElement('p', undefined, Adapter.description);
  const SourceCode = createElement(
    'code',
    undefined,
    `${Adapter.sourcePathHint}${Adapter.packageName ? ` -> ${Adapter.packageName}` : ''}`
  );
  const Actions = createElement('div', 'card-actions');
  const LoadButton = createElement('button', undefined, 'Load Adapter');
  const InspectButton = createElement('button', 'secondary', 'Send Envelope');

  LoadButton.addEventListener('click', async () =>
  {
    StatusChip.dataset.status = 'loading';
    StatusChip.textContent = 'loading';

    try
    {
      const Result = await loadAdapter(Adapter.id);
      StatusChip.dataset.status = Result.status === 'loaded' ? 'ready' : 'idle';
      StatusChip.textContent = Result.status;

      if (Adapter.phase === '1I' && Result.status === 'loaded')
      {
        await mountAnalyticsDemo();
      }
      if ((Adapter.phase === '1J' || Adapter.phase === '1E') && Result.status === 'loaded')
      {
        mountViewerDemo();
      }

      AppendLog(
        Adapter.repo,
        Result.note
          ? Result.note
          : `Loaded ${Result.details?.join(', ') || Adapter.id}.`
      );
    }
    catch (Error)
    {
      StatusChip.dataset.status = 'error';
      StatusChip.textContent = 'error';
      AppendLog(Adapter.repo, Error instanceof Error ? Error.message : 'Unknown load failure.');
    }
  });

  InspectButton.addEventListener('click', () =>
  {
    UEBridge.sendState({
      source: 'browser-runtime-shell',
      adapterId: Adapter.id,
      repo: Adapter.repo,
      activation: Adapter.activation
    });
    AppendLog(Adapter.repo, 'Sent adapter envelope to the Unreal bridge.');
  });

  Actions.append(LoadButton, InspectButton);
  CardElement.append(CardHeader, ActivationChip, Description, SourceCode, Actions);
  return CardElement;
}

function createAdapterSection(
  Adapters: BrowserSupportAdapter[],
  AppendLog: LogWriter
): HTMLElement
{
  const AdapterSection = createElement('section', 'section');
  AdapterSection.appendChild(
    createSectionHeader(
      'Phase 1 Adapter Surface',
      'Load any adapter to prove the runtime can resolve it, or inspect the activation type when a donor is intentionally retained as a sidecar.'
    )
  );

  const AdapterGrid = createElement('div', 'stack-grid');
  Adapters.forEach((Adapter) =>
  {
    AdapterGrid.appendChild(createAdapterCard(Adapter, AppendLog));
  });
  AdapterSection.appendChild(AdapterGrid);

  return AdapterSection;
}

function synchronizeCommandCounter(
  BootState: BrowserBootState,
  Counters: RuntimeCounters
): void
{
  Counters.commandReceiveCount = BootState.commandReceiveCount ?? (Counters.commandReceiveCount + 1);
}

function synchronizeShellStateCounter(
  BootState: BrowserBootState,
  Counters: RuntimeCounters
): void
{
  Counters.shellStateReceiveCount = BootState.shellStateReceiveCount ?? (Counters.shellStateReceiveCount + 1);
}

function wireBridgeSubscriptions(
  BootState: BrowserBootState,
  Counters: RuntimeCounters,
  AppendLog: LogWriter,
  RefreshRuntimeStatus: () => void,
  SetShellStatePayload: (Payload: unknown) => void
): void
{
  UEBridge.onCommand((Payload) =>
  {
    synchronizeCommandCounter(BootState, Counters);
    RefreshRuntimeStatus();
    AppendLog('UE Command', serializePretty(Payload));
  });

  UEBridge.onShellState((Payload) =>
  {
    synchronizeShellStateCounter(BootState, Counters);
    SetShellStatePayload(Payload);
    RefreshRuntimeStatus();
    AppendLog('Browser Shell State', 'Received fresh shell state from Unreal.');
  });
}

export function createBrowserShell(RootElement: HTMLElement): void
{
  const BootState = resolveBootState();
  const Adapters = listAdapters();
  const Counters: RuntimeCounters = {
    commandReceiveCount: BootState.commandReceiveCount ?? 0,
    shellStateReceiveCount: BootState.shellStateReceiveCount
      ?? (BootState.lastShellStateReceivedAtUtc ? 1 : 0)
  };

  RootElement.innerHTML = '';

  const ShellElement = createElement('div', 'shell');
  const HeroElement = createHeroSection(Adapters.length);
  const UtilitySection = createUtilitySection(BootState, Counters);
  const AdapterSection = createAdapterSection(Adapters, UtilitySection.appendLog);

  ShellElement.append(HeroElement, AdapterSection, UtilitySection.section);
  RootElement.appendChild(ShellElement);
  UtilitySection.refreshRuntimeStatus();

  wireBridgeSubscriptions(
    BootState,
    Counters,
    UtilitySection.appendLog,
    UtilitySection.refreshRuntimeStatus,
    UtilitySection.setShellStatePayload
  );
}
