import type {
  HassEntityAttributeBase,
  HassEntityBase,
} from "home-assistant-js-websocket";
import { timeCacheEntityPromiseFunc } from "../common/util/time-cache-entity-promise-func";
import type { HomeAssistant } from "../types";
import { getSignedPath } from "./auth";

export const CAMERA_ORIENTATIONS = [1, 2, 3, 4, 6, 8];

export const STREAM_TYPE_HLS = "hls";
export const STREAM_TYPE_WEB_RTC = "web_rtc";

export type StreamType = typeof STREAM_TYPE_HLS | typeof STREAM_TYPE_WEB_RTC;

export enum CameraEntityFeature {
  ON_OFF = 1,
  STREAM = 2,
}

interface CameraEntityAttributes extends HassEntityAttributeBase {
  model_name: string;
  access_token?: string;
  brand: string;
  motion_detection: boolean;
  frontend_stream_type: string;
}

export interface CameraEntity extends HassEntityBase {
  attributes: CameraEntityAttributes;
}

export interface CameraPreferences {
  preload_stream: boolean;
  orientation: number;
}

export interface CameraThumbnail {
  content_type: string;
  content: string;
}

export interface Stream {
  url: string;
}

export type WebRtcOfferEvent =
  | WebRtcId
  | WebRtcAnswer
  | WebRtcCandidate
  | WebRtcError;

export interface WebRtcId {
  type: "session";
  session_id: string;
}

export interface WebRtcAnswer {
  type: "answer";
  answer: string;
}

export interface WebRtcCandidate {
  type: "candidate";
  candidate: RTCIceCandidateInit;
}

export interface WebRtcError {
  type: "error";
  code: string;
  message: string;
}

export interface WebRtcOfferResponse {
  id: string;
}

export const cameraUrlWithWidthHeight = (
  base_url: string,
  width: number,
  height: number
) => `${base_url}&width=${width}&height=${height}`;

export const computeMJPEGStreamUrl = (
  entity: CameraEntity
): string | undefined =>
  entity.attributes.access_token
    ? `/api/camera_proxy_stream/${entity.entity_id}?token=${entity.attributes.access_token}`
    : undefined;

export const fetchThumbnailUrlWithCache = async (
  hass: Pick<HomeAssistant, "callWS" | "hassUrl">,
  entityId: string,
  width: number,
  height: number
) => {
  const base_url = await timeCacheEntityPromiseFunc(
    "_cameraTmbUrl",
    9000,
    fetchThumbnailUrl,
    hass,
    entityId
  );
  return cameraUrlWithWidthHeight(base_url, width, height);
};

export const fetchThumbnailUrl = async (
  hass: Pick<HomeAssistant, "callWS" | "hassUrl">,
  entityId: string
) => {
  const path = await getSignedPath(hass, `/api/camera_proxy/${entityId}`);
  return hass.hassUrl(path.path);
};

export const fetchStreamUrl = async (
  hass: Pick<HomeAssistant, "callWS" | "hassUrl">,
  entityId: string,
  format?: "hls"
) => {
  const data = {
    type: "camera/stream",
    entity_id: entityId,
  };
  if (format) {
    // @ts-ignore
    data.format = format;
  }
  const stream = await hass.callWS<Stream>(data);
  stream.url = hass.hassUrl(stream.url);
  return stream;
};

export const webRtcOffer = (
  hass: Pick<HomeAssistant, "connection">,
  entity_id: string,
  offer: string,
  callback: (event: WebRtcOfferEvent) => void
) =>
  hass.connection.subscribeMessage<WebRtcOfferEvent>(callback, {
    type: "camera/webrtc/offer",
    entity_id,
    offer,
  });

export const addWebRtcCandidate = (
  hass: Pick<HomeAssistant, "callWS">,
  entity_id: string,
  session_id: string,
  candidate: RTCIceCandidateInit
) =>
  hass.callWS({
    type: "camera/webrtc/candidate",
    entity_id,
    session_id,
    candidate: candidate,
  });

export const fetchCameraPrefs = (hass: HomeAssistant, entityId: string) =>
  hass.callWS<CameraPreferences>({
    type: "camera/get_prefs",
    entity_id: entityId,
  });

type ValueOf<T extends any[]> = T[number];
export const updateCameraPrefs = (
  hass: HomeAssistant,
  entityId: string,
  prefs: {
    preload_stream?: boolean;
    orientation?: ValueOf<typeof CAMERA_ORIENTATIONS>;
  }
) =>
  hass.callWS<CameraPreferences>({
    type: "camera/update_prefs",
    entity_id: entityId,
    ...prefs,
  });

const CAMERA_MEDIA_SOURCE_PREFIX = "media-source://camera/";

export const isCameraMediaSource = (mediaContentId: string) =>
  mediaContentId.startsWith(CAMERA_MEDIA_SOURCE_PREFIX);

export const getEntityIdFromCameraMediaSource = (mediaContentId: string) =>
  mediaContentId.substring(CAMERA_MEDIA_SOURCE_PREFIX.length);

export interface CameraCapabilities {
  frontend_stream_types: StreamType[];
}

export const fetchCameraCapabilities = async (
  hass: Pick<HomeAssistant, "callWS">,
  entity_id: string
) =>
  hass.callWS<CameraCapabilities>({ type: "camera/capabilities", entity_id });

export interface WebRTCClientConfiguration {
  configuration: RTCConfiguration;
  dataChannel?: string;
}

export const fetchWebRtcClientConfiguration = async (
  hass: Pick<HomeAssistant, "callWS">,
  entityId: string
) =>
  hass.callWS<WebRTCClientConfiguration>({
    type: "camera/webrtc/get_client_config",
    entity_id: entityId,
  });
