import type { IServiceCallRequest, IServiceCallResult } from '../../core/types.js';

export const agentDvrDomain = 'agent_dvr';
export const agentDvrDefaultPort = 8090;
export const agentDvrDefaultTimeoutMs = 10000;
export const agentDvrDefaultSnapshotTimeoutMs = 20000;
export const agentDvrCameraTypeId = 2;

export const agentDvrAlarmProfiles = ['home', 'away', 'night'] as const;

export type TAgentDvrProtocol = 'http' | 'https';
export type TAgentDvrSnapshotSource = 'http' | 'snapshot' | 'client' | 'manual' | 'offline';
export type TAgentDvrAlarmProfile = typeof agentDvrAlarmProfiles[number];
export type TAgentDvrAlarmState = 'disarmed' | 'armed_home' | 'armed_away' | 'armed_night' | 'armed_custom_bypass' | 'unknown';

export type TAgentDvrCommandType =
  | 'refresh'
  | 'status'
  | 'stream_source'
  | 'snapshot_image'
  | 'camera_command'
  | 'alarm_command'
  | 'set_profile';

export type TAgentDvrCameraCommandName =
  | 'switchOn'
  | 'switchOff'
  | 'record'
  | 'recordStop'
  | 'alertOn'
  | 'alertOff'
  | 'detectorOn'
  | 'detectorOff'
  | 'snapshot';

export type TAgentDvrAlarmCommandName = 'arm' | 'disarm' | 'setProfileByName';

export interface IAgentDvrConfig {
  protocol?: TAgentDvrProtocol;
  host?: string;
  port?: number;
  url?: string;
  serverUrl?: string;
  timeoutMs?: number;
  snapshotTimeoutMs?: number;
  name?: string;
  uniqueId?: string;
  connected?: boolean;
  status?: Partial<IAgentDvrServerSnapshot> & Record<string, unknown>;
  locations?: IAgentDvrLocation[];
  profiles?: IAgentDvrProfile[];
  devices?: Array<IAgentDvrDeviceSnapshot | IAgentDvrRawDevice>;
  snapshot?: IAgentDvrSnapshot;
  client?: IAgentDvrClientLike;
  commandExecutor?: IAgentDvrCommandExecutor;
  metadata?: Record<string, unknown>;
}

export interface IHomeAssistantAgentDvrConfig extends IAgentDvrConfig {}

export interface IAgentDvrServerSnapshot {
  id?: string;
  unique?: string;
  name?: string;
  host?: string;
  port?: number;
  protocol?: TAgentDvrProtocol;
  url?: string;
  serverUrl?: string;
  version?: string;
  remoteAccess?: boolean;
  deviceCount?: number;
  armed?: boolean | null;
  activeProfile?: string | null;
  available?: boolean;
  locations?: IAgentDvrLocation[];
  rawStatus?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
}

export interface IAgentDvrLocation {
  name?: string;
  id?: string | number;
  [key: string]: unknown;
}

export interface IAgentDvrProfile {
  name?: string;
  active?: boolean;
  id?: string | number;
  ind?: number;
  index?: number;
  [key: string]: unknown;
}

export interface IAgentDvrDeviceData {
  recording?: boolean;
  alerted?: boolean;
  detected?: boolean;
  online?: boolean;
  alertsActive?: boolean;
  detectorActive?: boolean;
  connected?: boolean;
  ptzid?: number;
  width?: number;
  height?: number;
  mjpegStreamWidth?: number;
  mjpegStreamHeight?: number;
  [key: string]: unknown;
}

export interface IAgentDvrRawDevice {
  id?: string | number;
  oid?: string | number;
  typeID?: string | number;
  typeId?: string | number;
  ot?: string | number;
  name?: string;
  locationIndex?: number;
  data?: IAgentDvrDeviceData;
  [key: string]: unknown;
}

export interface IAgentDvrDeviceUrls {
  stillImageUrl?: string;
  mjpegImageUrl?: string;
  mp4Url?: string;
  webmUrl?: string;
  streamSourceUrl?: string;
}

export interface IAgentDvrDeviceSnapshot {
  id: string | number;
  objectId: number;
  typeId: number;
  name: string;
  location?: string;
  locationIndex?: number;
  data: IAgentDvrDeviceData;
  urls: IAgentDvrDeviceUrls;
  available: boolean;
  raw?: IAgentDvrRawDevice | Record<string, unknown>;
  attributes?: Record<string, unknown>;
}

export interface IAgentDvrSnapshot {
  connected: boolean;
  server: IAgentDvrServerSnapshot;
  devices: IAgentDvrDeviceSnapshot[];
  locations: IAgentDvrLocation[];
  profiles: IAgentDvrProfile[];
  updatedAt?: string;
  source?: TAgentDvrSnapshotSource;
  error?: string;
  metadata?: Record<string, unknown>;
}

export interface IAgentDvrSnapshotImage {
  contentType: string;
  data: Uint8Array;
}

export interface IAgentDvrHttpCommand {
  label: string;
  path: 'command.cgi';
  params: Record<string, string | number | boolean>;
  expect: 'json';
}

export interface IAgentDvrCommandRequest {
  type: TAgentDvrCommandType;
  service: string;
  target?: IServiceCallRequest['target'];
  data?: Record<string, unknown>;
  objectId?: number;
  objectTypeId?: number;
  command?: TAgentDvrCameraCommandName | TAgentDvrAlarmCommandName;
  profile?: TAgentDvrAlarmProfile | string;
  filename?: string;
  size?: string;
  httpCommands?: IAgentDvrHttpCommand[];
}

export interface IAgentDvrCommandResult extends IServiceCallResult {
  transmitted?: boolean;
  command?: IAgentDvrCommandRequest;
  responses?: unknown[];
}

export interface IAgentDvrClientLike {
  getSnapshot?(): Promise<IAgentDvrSnapshot | Partial<IAgentDvrSnapshot> | Record<string, unknown>>;
  getStatus?(): Promise<Record<string, unknown>>;
  getObjects?(): Promise<Record<string, unknown> | IAgentDvrRawDevice[]>;
  getProfiles?(): Promise<Record<string, unknown> | IAgentDvrProfile[]>;
  getSnapshotImage?(deviceArg: IAgentDvrDeviceSnapshot | undefined): Promise<IAgentDvrSnapshotImage>;
  execute?(commandArg: IAgentDvrCommandRequest): Promise<unknown>;
  close?(): Promise<void> | void;
}

export interface IAgentDvrCommandExecutor {
  execute(commandArg: IAgentDvrCommandRequest): Promise<unknown>;
}

export interface IAgentDvrManualEntry {
  integrationDomain?: string;
  id?: string;
  host?: string;
  port?: number;
  url?: string;
  serverUrl?: string;
  protocol?: TAgentDvrProtocol;
  name?: string;
  uniqueId?: string;
  manufacturer?: string;
  model?: string;
  serialNumber?: string;
  macAddress?: string;
  snapshot?: IAgentDvrSnapshot;
  metadata?: Record<string, unknown>;
}

export interface IAgentDvrMdnsRecord {
  type?: string;
  name?: string;
  host?: string;
  port?: number;
  addresses?: string[];
  hostname?: string;
  txt?: Record<string, string | undefined>;
  properties?: Record<string, string | undefined>;
}

export interface IAgentDvrSsdpRecord {
  manufacturer?: string;
  server?: string;
  st?: string;
  usn?: string;
  location?: string;
  upnp?: Record<string, string | undefined>;
  headers?: Record<string, string | undefined>;
}

export interface IAgentDvrCandidateMetadata extends Record<string, unknown> {
  agentDvr?: boolean;
  ispy?: boolean;
  discoveryProtocol?: 'manual' | 'mdns' | 'ssdp';
  protocol?: TAgentDvrProtocol;
  url?: string;
  serverUrl?: string;
  snapshot?: IAgentDvrSnapshot;
}
