import type { ContextId } from '../use/use-context';
import { trackSignalAndAssignHost } from '../use/use-core';
import { version } from '../version';
import type { SubscriptionData } from '../reactive-primitives/subscription-data';
import type { Signal } from '../reactive-primitives/signal.public';
import type {
  SSRInternalStreamWriter,
  StreamWriter,
  SymbolToChunkResolver,
} from '../ssr/ssr-types';
import {
  createSerializationContext,
  type SerializationContext,
} from './serdes/serialization-context';
import type { Container, HostElement, ObjToProxyMap } from './types';

/** @internal */
export abstract class _SharedContainer implements Container {
  readonly $version$: string;
  readonly $storeProxyMap$: ObjToProxyMap;
  $rootContainer$: Container | null = null;
  $isOutOfOrderSegment$ = false;
  /// Current language locale
  readonly $locale$: string;
  /// Retrieve Object from paused serialized state.
  readonly $getObjectById$: (id: number | string) => any;
  $serverData$: Record<string, any>;
  $currentUniqueId$ = 0;
  $instanceHash$: string | null = null;
  $buildBase$: string | null = null;
  $renderPromise$: Promise<void> | null = null;
  $resolveRenderPromise$: (() => void) | null = null;
  $pendingCount$: number = 0;

  constructor(serverData: Record<string, any>, locale: string) {
    this.$serverData$ = serverData;
    this.$locale$ = locale;
    this.$version$ = version;
    this.$storeProxyMap$ = new WeakMap();
    this.$getObjectById$ = (_id: number | string) => {
      throw Error('Not implemented');
    };
  }

  trackSignalValue<T>(
    signal: Signal,
    subscriber: HostElement,
    property: string,
    data: SubscriptionData
  ): T {
    return trackSignalAndAssignHost(signal, subscriber, property, this, data);
  }

  serializationCtxFactory(
    NodeConstructor: {
      new (...rest: any[]): { __brand__: 'SsrNode' };
    } | null,
    DomRefConstructor: {
      new (...rest: any[]): { __brand__: 'DomRef' };
    } | null,
    symbolToChunkResolver: SymbolToChunkResolver,
    writer?: StreamWriter
  ): SerializationContext {
    return createSerializationContext(
      NodeConstructor,
      DomRefConstructor,
      symbolToChunkResolver,
      this.setHostProp.bind(this),
      this.$storeProxyMap$,
      writer as SSRInternalStreamWriter | undefined
    );
  }

  $checkPendingCount$(): void {
    if (this.$pendingCount$ === 0) {
      this.$resolveRenderPromise$?.();
      this.$renderPromise$ = null;
    }
  }

  abstract ensureProjectionResolved(host: HostElement): void;
  abstract handleError(err: any, $host$: HostElement | null): void;
  abstract getParentHost(host: HostElement): HostElement | null;
  abstract setContext<T>(host: HostElement, context: ContextId<T>, value: T): void;
  abstract resolveContext<T>(host: HostElement, contextId: ContextId<T>): T | undefined;
  abstract setHostProp<T>(host: HostElement, name: string, value: T): void;
  abstract getHostProp<T>(host: HostElement, name: string): T | null;
  abstract $appendStyle$(
    content: string,
    styleId: string,
    host: HostElement,
    scoped: boolean
  ): void;
}
