import { createContext, useContext, useEffect, type ReactNode } from "react";

import {
  MissionRuntimeProvider,
  useMissionRuntime,
  type DeviceModelDefinition,
  type MissionRuntimeController,
  type MissionRuntimeState,
} from "@mission-core/plugin-sdk";
import {
  confirmedRuntimeSourceMode,
  effectiveAcquisition,
  normalizeRuntimePhase,
  spatialSourceId,
} from "./lifecycle";
import { localizeRuntimeMessage } from "./messages";
import { xgridsK1Manifest } from "./manifest";
import { deviceTelemetry, finiteMetric, pipelineLatency } from "./presentation";
import { xgridsK1ObservationSources } from "./observationSources";
import { useXgridsK1Runtime } from "./useXgridsK1Runtime";

export type XgridsK1Controller = ReturnType<typeof useXgridsK1Runtime>;

const XgridsK1RuntimeContext = createContext<XgridsK1Controller | null>(null);

function normalizeState(
  controller: XgridsK1Controller,
  activeModel: DeviceModelDefinition,
): MissionRuntimeState | null {
  const state = controller.state;
  if (!state) return null;
  const metrics = state.metrics;
  const telemetry = deviceTelemetry(metrics);
  const deviceRef = state.device_ref;
  const deviceSession = state.device_session;
  const acquisition = effectiveAcquisition(state);
  const sourceUrl = state.rerun_grpc_url?.trim() ?? "";
  const resolvedSpatialSourceId =
    state.source_mode === "replay"
      ? spatialSourceId(state, sourceUrl)
      : acquisition?.acquisition_id ?? spatialSourceId(state, sourceUrl);

  return {
    phase: normalizeRuntimePhase(state),
    message: localizeRuntimeMessage(state.message),
    activeDevice: deviceRef
      ? {
          pluginId: xgridsK1Manifest.metadata.id,
          modelId: deviceRef.model_id || activeModel.id,
          displayName: activeModel.displayName,
          instanceId: deviceRef.device_id,
          endpointLabel: state.k1_ip,
        }
      : null,
    deviceSession: deviceSession
      ? {
          sessionId: deviceSession.device_session_id,
          deviceId: deviceSession.device_id,
          compatibilityProfileId: deviceSession.compatibility_profile_id,
          connectivity: deviceSession.connectivity,
        }
      : null,
    acquisition: acquisition
      ? {
          acquisitionId: acquisition.acquisition_id,
          deviceId: acquisition.device_id,
          deviceSessionId: acquisition.device_session_id,
          compatibilityProfileId: acquisition.compatibility_profile_id,
          controlMode: acquisition.control_mode,
          state: acquisition.state,
          stateRevision: acquisition.state_revision,
          operatorInstructions: acquisition.operator_instructions ?? [],
          cleanupPending: acquisition.cleanup_pending === true,
        }
      : null,
    operations: (state.operations ?? []).map((operation) => ({
      operationId: operation.operation_id,
      action: operation.action,
      status: operation.status,
      stageCode: operation.stage_code,
      messageCode: operation.message_code,
    })),
    spatialSource: sourceUrl && resolvedSpatialSourceId
      ? {
          id: resolvedSpatialSourceId,
          url: sourceUrl,
          label:
            state.source_mode === "replay"
              ? "Повтор пространственной записи"
              : "Локальный пространственный поток",
          kind: "rerun-grpc",
        }
      : null,
    observationSources: xgridsK1ObservationSources(state, activeModel),
    observationTimeline: {
      // This is the shared host timeline. A replayable Rerun source alone does
      // not make camera and point-cloud time jointly seekable.
      mode: "live-only",
      seekable: false,
      sessionRecording: false,
      synchronization: "host-arrival-best-effort",
      range: null,
    },
    viewerSettings: state.viewer_settings,
    sourceMode: confirmedRuntimeSourceMode(state),
    metrics: {
      latencyMs: pipelineLatency(metrics),
      frameRateHz: finiteMetric(metrics?.frame_rate ?? metrics?.frame_rate_hz),
      pointCount: finiteMetric(metrics?.point_count),
      droppedPreviewFrames: finiteMetric(metrics?.dropped_preview_frames),
      aiLatencyMs: finiteMetric(metrics?.ai_end_to_end_ms),
      aiFrameRateHz: finiteMetric(metrics?.ai_frame_rate_hz),
      aiDroppedFrames: finiteMetric(metrics?.ai_dropped_frames),
      aiStaleMs: finiteMetric(metrics?.ai_stale_ms),
      elapsedSeconds: telemetry.elapsedSeconds,
      routeDistanceMeters: telemetry.routeDistanceMeters,
      speedMetersPerSecond: telemetry.speedMetersPerSecond,
    },
  };
}

export function XgridsK1RuntimeProvider({
  activeModel,
  registerDeactivation,
  children,
}: {
  activeModel: DeviceModelDefinition | null;
  registerDeactivation: (handler: () => Promise<boolean>) => () => void;
  children: ReactNode;
}) {
  const active = activeModel !== null;
  const inheritedRuntime = useMissionRuntime();
  const controller = useXgridsK1Runtime(active);
  const missionRuntime: MissionRuntimeController = {
    state: activeModel ? normalizeState(controller, activeModel) : null,
    backendStatus: controller.backendStatus,
    pendingAction: controller.pendingAction,
    refresh: controller.refresh,
    updateViewerSettings: controller.updateViewerSettings,
    setObservationSourceActive: controller.setObservationSourceActive,
  };

  useEffect(() => {
    if (!activeModel) return;
    return registerDeactivation(async () => {
      if (controller.pendingAction !== null) return false;
      // Never translate navigation/model switching into a K1 control command.
      // The operator must explicitly finish or close the canonical dialogue.
      if (controller.state?.application_control_session?.control_socket_open) {
        return false;
      }
      return controller.stop();
    });
  }, [
    activeModel,
    controller.pendingAction,
    controller.state?.application_control_session?.control_socket_open,
    controller.stop,
    registerDeactivation,
  ]);

  return (
    <XgridsK1RuntimeContext.Provider value={active ? controller : null}>
      <MissionRuntimeProvider value={active ? missionRuntime : inheritedRuntime}>
        {children}
      </MissionRuntimeProvider>
    </XgridsK1RuntimeContext.Provider>
  );
}

export function useXgridsK1Controller(): XgridsK1Controller {
  const controller = useContext(XgridsK1RuntimeContext);
  if (!controller) {
    throw new Error("XGRIDS K1 runtime используется вне собственного plugin provider.");
  }
  return controller;
}
