import type {
  IAgentDvrClientLike,
  IAgentDvrCommandRequest,
  IAgentDvrCommandResult,
  IAgentDvrConfig,
  IAgentDvrDeviceData,
  IAgentDvrDeviceSnapshot,
  IAgentDvrLocation,
  IAgentDvrProfile,
  IAgentDvrRawDevice,
  IAgentDvrServerSnapshot,
  IAgentDvrSnapshot,
  IAgentDvrSnapshotImage,
  TAgentDvrProtocol,
} from './agent_dvr.types.js';
import {
  agentDvrCameraTypeId,
  agentDvrDefaultPort,
  agentDvrDefaultSnapshotTimeoutMs,
  agentDvrDefaultTimeoutMs,
} from './agent_dvr.types.js';

export class AgentDvrApiError extends Error {}

export class AgentDvrApiConnectionError extends AgentDvrApiError {}

export class AgentDvrApiCommandError extends AgentDvrApiError {
  constructor(public readonly command: IAgentDvrCommandRequest, messageArg: string) {
    super(messageArg);
    this.name = 'AgentDvrApiCommandError';
  }
}

export class AgentDvrClient {
  private currentSnapshot?: IAgentDvrSnapshot;

  constructor(private readonly config: IAgentDvrConfig) {}

  public async getSnapshot(forceRefreshArg = false): Promise<IAgentDvrSnapshot> {
    if (!forceRefreshArg && this.currentSnapshot) {
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (!forceRefreshArg && this.config.snapshot) {
      this.currentSnapshot = this.normalizeSnapshot(this.cloneSnapshot(this.config.snapshot), 'snapshot');
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (this.config.client) {
      try {
        this.currentSnapshot = await this.snapshotFromClient(this.config.client);
      } catch (errorArg) {
        this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
      }
      return this.cloneSnapshot(this.currentSnapshot);
    }

    if (this.hasLiveTarget()) {
      try {
        this.currentSnapshot = await this.fetchLiveSnapshot();
      } catch (errorArg) {
        this.currentSnapshot = this.offlineSnapshot(this.errorMessage(errorArg));
      }
      return this.cloneSnapshot(this.currentSnapshot);
    }

    this.currentSnapshot = this.snapshotFromConfig(this.config.connected ?? false, undefined, this.hasManualData() ? 'manual' : 'offline');
    return this.cloneSnapshot(this.currentSnapshot);
  }

  public async refresh(): Promise<IAgentDvrCommandResult> {
    if (this.config.client) {
      try {
        this.currentSnapshot = await this.snapshotFromClient(this.config.client);
        return { success: this.currentSnapshot.connected && !this.currentSnapshot.error, transmitted: false, data: { snapshot: this.cloneSnapshot(this.currentSnapshot), source: 'client' } };
      } catch (errorArg) {
        const error = this.errorMessage(errorArg);
        this.currentSnapshot = this.offlineSnapshot(error);
        return { success: false, transmitted: false, error, data: { snapshot: this.cloneSnapshot(this.currentSnapshot) } };
      }
    }

    if (!this.hasLiveTarget()) {
      const snapshot = await this.getSnapshot();
      return {
        success: false,
        transmitted: false,
        snapshot,
        error: 'Agent DVR refresh requires a configured local HTTP endpoint or injected client.',
        data: { snapshot },
      } as IAgentDvrCommandResult & { snapshot: IAgentDvrSnapshot };
    }

    try {
      this.currentSnapshot = await this.fetchLiveSnapshot();
      return { success: true, transmitted: true, data: { snapshot: this.cloneSnapshot(this.currentSnapshot), source: 'http' } };
    } catch (errorArg) {
      const error = this.errorMessage(errorArg);
      this.currentSnapshot = this.offlineSnapshot(error);
      return { success: false, transmitted: true, error, data: { snapshot: this.cloneSnapshot(this.currentSnapshot) } };
    }
  }

  public async validateConnection(): Promise<IAgentDvrSnapshot> {
    if (!this.hasLiveTarget()) {
      throw new AgentDvrApiConnectionError('Agent DVR validation requires a local HTTP endpoint.');
    }
    return this.fetchLiveSnapshot();
  }

  public async getSnapshotImage(deviceArg?: IAgentDvrDeviceSnapshot, sizeArg?: string): Promise<IAgentDvrSnapshotImage> {
    if (!this.hasLiveTarget()) {
      throw new AgentDvrApiConnectionError('Agent DVR image snapshots require a configured local HTTP endpoint.');
    }
    const oid = deviceArg?.objectId;
    const response = await this.request('grab.jpg', this.cleanParams({ oid, size: sizeArg }), this.config.snapshotTimeoutMs || agentDvrDefaultSnapshotTimeoutMs);
    if (!response.ok) {
      throw new AgentDvrApiConnectionError(`Agent DVR grab.jpg returned HTTP ${response.status}.`);
    }
    return {
      contentType: response.headers.get('content-type') || 'image/jpeg',
      data: new Uint8Array(await response.arrayBuffer()),
    };
  }

  public async execute(commandArg: IAgentDvrCommandRequest): Promise<unknown> {
    if (this.config.commandExecutor) {
      return this.config.commandExecutor.execute(commandArg);
    }

    if (this.config.client && !this.hasLiveTarget()) {
      return this.executeWithClient(this.config.client, commandArg);
    }

    if (commandArg.type === 'refresh' || commandArg.type === 'status') {
      const result = await this.refresh();
      return commandArg.type === 'status' ? result.data : result;
    }

    if (commandArg.type === 'stream_source') {
      return this.streamSource(commandArg);
    }

    if (!this.hasLiveTarget()) {
      throw new AgentDvrApiConnectionError('Agent DVR commands require config.host, config.url, config.serverUrl, an injected client, or commandExecutor.');
    }

    if (commandArg.type === 'snapshot_image') {
      if (commandArg.filename) {
        throw new AgentDvrApiError('Agent DVR snapshot file writes are not implemented; request image data without data.filename.');
      }
      const snapshot = await this.getSnapshot();
      const device = this.findDevice(snapshot, commandArg.objectId, commandArg.objectTypeId);
      if ((commandArg.objectId !== undefined || commandArg.objectTypeId !== undefined) && !device) {
        throw new AgentDvrApiCommandError(commandArg, 'Agent DVR snapshot target was not found.');
      }
      const image = await this.getSnapshotImage(device, commandArg.size);
      return { ok: true, transmitted: true, contentType: image.contentType, dataBase64: Buffer.from(image.data).toString('base64') };
    }

    if (!commandArg.httpCommands?.length) {
      throw new AgentDvrApiCommandError(commandArg, `Unsupported Agent DVR command: ${commandArg.type}`);
    }

    const responses: unknown[] = [];
    for (const httpCommand of commandArg.httpCommands) {
      const response = await this.getJson(httpCommand.path, httpCommand.params);
      if (this.responseIsFailure(response)) {
        throw new AgentDvrApiCommandError(commandArg, `Agent DVR command ${String(httpCommand.params.cmd)} reported failure.`);
      }
      responses.push(response);
    }
    this.patchCachedSnapshot(commandArg);
    return { ok: true, success: true, transmitted: true, command: commandArg, responses };
  }

  public async destroy(): Promise<void> {
    await this.config.client?.close?.();
  }

  private async fetchLiveSnapshot(): Promise<IAgentDvrSnapshot> {
    const status = await this.commandJson('getStatus');
    const [objects, profiles] = await Promise.all([
      this.commandJson('getObjects').catch(() => undefined),
      this.commandJson('getProfiles').catch(() => undefined),
    ]);
    return this.snapshotFromLiveData(status, objects, profiles);
  }

  private snapshotFromLiveData(statusArg: Record<string, unknown>, objectsArg?: Record<string, unknown>, profilesArg?: Record<string, unknown>): IAgentDvrSnapshot {
    const objectList = Array.isArray(objectsArg?.objectList) ? objectsArg.objectList as IAgentDvrRawDevice[] : [];
    const locations = Array.isArray(objectsArg?.locations) ? objectsArg.locations as IAgentDvrLocation[] : this.config.locations || [];
    const profiles = this.profilesFromRaw(profilesArg) || this.config.profiles || [];
    const server = this.serverFromStatus(statusArg, true, locations, profiles);
    return this.normalizeSnapshot({
      connected: true,
      server,
      devices: objectList.map((deviceArg) => this.normalizeDevice(deviceArg, locations, true)),
      locations,
      profiles,
      updatedAt: new Date().toISOString(),
      source: 'http',
      metadata: this.config.metadata,
    }, 'http');
  }

  private async snapshotFromClient(clientArg: IAgentDvrClientLike): Promise<IAgentDvrSnapshot> {
    if (clientArg.getSnapshot) {
      const result = await clientArg.getSnapshot();
      if (this.isSnapshot(result)) {
        return this.normalizeSnapshot(result as IAgentDvrSnapshot, 'client');
      }
      if (result && typeof result === 'object') {
        return this.normalizeSnapshot({
          ...this.snapshotFromConfig(true, undefined, 'client'),
          ...(result as Partial<IAgentDvrSnapshot>),
        }, 'client');
      }
    }

    const status = clientArg.getStatus ? await clientArg.getStatus() : this.config.status || {};
    const rawObjects = clientArg.getObjects ? await clientArg.getObjects() : this.config.devices || [];
    const rawProfiles = clientArg.getProfiles ? await clientArg.getProfiles() : this.config.profiles || [];
    const objects = Array.isArray(rawObjects) ? { objectList: rawObjects, locations: this.config.locations || [] } : rawObjects;
    const profiles = Array.isArray(rawProfiles) ? { profiles: rawProfiles } : rawProfiles;
    return this.snapshotFromLiveData(status, objects as Record<string, unknown>, profiles as Record<string, unknown>);
  }

  private snapshotFromConfig(connectedArg: boolean, errorArg?: string, sourceArg: IAgentDvrSnapshot['source'] = 'manual'): IAgentDvrSnapshot {
    const locations = this.config.locations || this.config.snapshot?.locations || [];
    const profiles = this.config.profiles || this.config.snapshot?.profiles || [];
    const status = this.config.status || this.config.snapshot?.server?.rawStatus || {};
    const devices = this.config.devices || this.config.snapshot?.devices || [];
    return this.normalizeSnapshot({
      connected: connectedArg,
      server: this.serverFromStatus(status, connectedArg, locations, profiles),
      devices: devices.map((deviceArg) => this.normalizeDevice(deviceArg, locations, connectedArg)),
      locations,
      profiles,
      updatedAt: new Date().toISOString(),
      source: sourceArg,
      error: errorArg,
      metadata: this.config.metadata || this.config.snapshot?.metadata,
    }, sourceArg);
  }

  private offlineSnapshot(errorArg: string): IAgentDvrSnapshot {
    return this.snapshotFromConfig(false, errorArg, 'offline');
  }

  private normalizeSnapshot(snapshotArg: IAgentDvrSnapshot, sourceArg: IAgentDvrSnapshot['source'] = snapshotArg.source): IAgentDvrSnapshot {
    const locations = snapshotArg.locations || snapshotArg.server.locations || this.config.locations || [];
    const profiles = snapshotArg.profiles || this.config.profiles || [];
    const connected = Boolean(snapshotArg.connected && snapshotArg.server.available !== false);
    const server = {
      ...this.serverFromStatus(snapshotArg.server.rawStatus || this.config.status || {}, connected, locations, profiles),
      ...snapshotArg.server,
      available: connected,
      locations,
    };
    return {
      ...snapshotArg,
      connected,
      server,
      devices: (snapshotArg.devices || []).map((deviceArg) => this.normalizeDevice(deviceArg, locations, connected)),
      locations,
      profiles,
      updatedAt: snapshotArg.updatedAt || new Date().toISOString(),
      source: sourceArg,
    };
  }

  private serverFromStatus(statusArg: Partial<IAgentDvrServerSnapshot> & Record<string, unknown>, connectedArg: boolean, locationsArg: IAgentDvrLocation[], profilesArg: IAgentDvrProfile[]): IAgentDvrServerSnapshot {
    const endpoint = this.endpoint();
    const name = stringValue(statusArg.name) || this.config.name || this.config.snapshot?.server.name || endpoint.host || 'Agent DVR';
    const unique = stringValue(statusArg.unique) || this.config.uniqueId || this.config.snapshot?.server.unique || endpoint.host || 'agent-dvr';
    const activeProfile = stringValue(statusArg.activeProfile) || this.activeProfile(profilesArg) || this.config.snapshot?.server.activeProfile || null;
    return {
      id: unique,
      unique,
      name,
      host: endpoint.host,
      port: endpoint.port,
      protocol: endpoint.protocol,
      url: endpoint.url,
      serverUrl: endpoint.url,
      version: stringValue(statusArg.version) || this.config.snapshot?.server.version,
      remoteAccess: booleanValue(statusArg.remoteAccess) ?? this.config.snapshot?.server.remoteAccess,
      deviceCount: numberValue(statusArg.devices) ?? numberValue(statusArg.deviceCount) ?? this.config.snapshot?.server.deviceCount,
      armed: booleanValue(statusArg.armed) ?? this.config.snapshot?.server.armed ?? null,
      activeProfile,
      available: connectedArg,
      locations: locationsArg,
      rawStatus: statusArg,
      metadata: {
        ...this.config.snapshot?.server.metadata,
        ...this.config.metadata,
      },
    };
  }

  private normalizeDevice(deviceArg: IAgentDvrDeviceSnapshot | IAgentDvrRawDevice, locationsArg: IAgentDvrLocation[], connectedArg: boolean): IAgentDvrDeviceSnapshot {
    const raw = (this.isDeviceSnapshot(deviceArg) ? deviceArg.raw || deviceArg : deviceArg) as IAgentDvrRawDevice;
    const rawData = { ...(raw.data || {}), ...(this.isDeviceSnapshot(deviceArg) ? deviceArg.data : {}) } as IAgentDvrDeviceData;
    const objectId = numberValue(raw.id) ?? numberValue(raw.oid) ?? numberValue(this.isDeviceSnapshot(deviceArg) ? deviceArg.objectId : undefined) ?? 0;
    const typeId = numberValue(raw.typeID) ?? numberValue(raw.typeId) ?? numberValue(raw.ot) ?? numberValue(this.isDeviceSnapshot(deviceArg) ? deviceArg.typeId : undefined) ?? agentDvrCameraTypeId;
    const locationIndex = numberValue(raw.locationIndex) ?? (this.isDeviceSnapshot(deviceArg) ? deviceArg.locationIndex : undefined);
    const location = this.locationName(locationsArg, locationIndex) || (this.isDeviceSnapshot(deviceArg) ? deviceArg.location : undefined);
    const online = booleanValue(rawData.online) ?? connectedArg;
    const deviceConnected = booleanValue(rawData.connected) ?? online;
    const available = connectedArg && online && deviceConnected;
    const width = numberValue(rawData.width) ?? 640;
    const height = numberValue(rawData.height) ?? 480;
    const streamWidth = numberValue(rawData.mjpegStreamWidth) ?? 640;
    const streamHeight = numberValue(rawData.mjpegStreamHeight) ?? 480;
    const urls = this.deviceUrls(objectId, typeId, streamWidth, streamHeight, this.isDeviceSnapshot(deviceArg) ? deviceArg.urls : undefined);

    return {
      id: raw.id ?? objectId,
      objectId,
      typeId,
      name: stringValue(raw.name) || (this.isDeviceSnapshot(deviceArg) ? deviceArg.name : undefined) || `Agent DVR ${typeId === agentDvrCameraTypeId ? 'Camera' : 'Device'} ${objectId || ''}`.trim(),
      location,
      locationIndex,
      data: {
        ...rawData,
        online,
        connected: deviceConnected,
        recording: booleanValue(rawData.recording) ?? false,
        alerted: booleanValue(rawData.alerted) ?? false,
        detected: booleanValue(rawData.detected) ?? false,
        alertsActive: booleanValue(rawData.alertsActive) ?? false,
        detectorActive: booleanValue(rawData.detectorActive) ?? false,
        ptzid: numberValue(rawData.ptzid) ?? -1,
        width,
        height,
        mjpegStreamWidth: streamWidth,
        mjpegStreamHeight: streamHeight,
      },
      urls,
      available,
      raw,
      attributes: this.isDeviceSnapshot(deviceArg) ? deviceArg.attributes : undefined,
    };
  }

  private deviceUrls(objectIdArg: number, typeIdArg: number, widthArg: number, heightArg: number, existingArg?: IAgentDvrDeviceSnapshot['urls']): IAgentDvrDeviceSnapshot['urls'] {
    const base = this.safeBaseUrl();
    if (!base || typeIdArg !== agentDvrCameraTypeId || !objectIdArg) {
      return existingArg || {};
    }
    const size = `${widthArg}x${heightArg}`;
    return {
      ...existingArg,
      stillImageUrl: existingArg?.stillImageUrl || this.urlString('grab.jpg', { oid: objectIdArg, size }),
      mjpegImageUrl: existingArg?.mjpegImageUrl || this.urlString('video.mjpg', { oid: objectIdArg, size }),
      mp4Url: existingArg?.mp4Url || this.urlString('video.mp4', { oid: objectIdArg, size }),
      webmUrl: existingArg?.webmUrl || this.urlString('video.webm', { oid: objectIdArg, size }),
      streamSourceUrl: existingArg?.streamSourceUrl || this.urlString('video.mjpg', { oid: objectIdArg, size }),
    };
  }

  private async executeWithClient(clientArg: IAgentDvrClientLike, commandArg: IAgentDvrCommandRequest): Promise<unknown> {
    if (clientArg.execute) {
      return clientArg.execute(commandArg);
    }
    if ((commandArg.type === 'refresh' || commandArg.type === 'status') && clientArg.getSnapshot) {
      return clientArg.getSnapshot();
    }
    if (commandArg.type === 'snapshot_image' && clientArg.getSnapshotImage) {
      const snapshot = await this.getSnapshot();
      const image = await clientArg.getSnapshotImage(this.findDevice(snapshot, commandArg.objectId, commandArg.objectTypeId));
      return { ok: true, transmitted: true, contentType: image.contentType, dataBase64: Buffer.from(image.data).toString('base64') };
    }
    throw new AgentDvrApiConnectionError('Agent DVR command is not available on the injected client and no local HTTP endpoint or commandExecutor is configured.');
  }

  private async streamSource(commandArg: IAgentDvrCommandRequest): Promise<Record<string, unknown>> {
    const snapshot = await this.getSnapshot();
    const device = this.findDevice(snapshot, commandArg.objectId, commandArg.objectTypeId);
    if ((commandArg.objectId !== undefined || commandArg.objectTypeId !== undefined) && !device) {
      throw new AgentDvrApiCommandError(commandArg, 'Agent DVR stream source target was not found.');
    }
    return {
      objectId: device?.objectId,
      objectTypeId: device?.typeId,
      streamSourceUrl: device?.urls.streamSourceUrl,
      mjpegImageUrl: device?.urls.mjpegImageUrl,
      stillImageUrl: device?.urls.stillImageUrl,
      verified: false,
    };
  }

  private patchCachedSnapshot(commandArg: IAgentDvrCommandRequest): void {
    if (!this.currentSnapshot) {
      return;
    }
    if (commandArg.type === 'alarm_command' || commandArg.type === 'set_profile') {
      const profile = commandArg.profile;
      if (commandArg.command === 'disarm') {
        this.currentSnapshot.server.armed = false;
      } else if (commandArg.command === 'arm') {
        this.currentSnapshot.server.armed = true;
      }
      if (profile) {
        this.currentSnapshot.server.activeProfile = profile;
      }
      this.currentSnapshot.updatedAt = new Date().toISOString();
      return;
    }
    const device = this.findDevice(this.currentSnapshot, commandArg.objectId, commandArg.objectTypeId);
    if (!device) {
      return;
    }
    if (commandArg.command === 'switchOn') {
      device.data.online = true;
    } else if (commandArg.command === 'switchOff') {
      device.data.online = false;
    } else if (commandArg.command === 'record') {
      device.data.recording = true;
    } else if (commandArg.command === 'recordStop') {
      device.data.recording = false;
    } else if (commandArg.command === 'alertOn') {
      device.data.alertsActive = true;
    } else if (commandArg.command === 'alertOff') {
      device.data.alertsActive = false;
    } else if (commandArg.command === 'detectorOn') {
      device.data.detectorActive = true;
    } else if (commandArg.command === 'detectorOff') {
      device.data.detectorActive = false;
    }
    device.available = Boolean(this.currentSnapshot.connected && device.data.online && device.data.connected);
    this.currentSnapshot.updatedAt = new Date().toISOString();
  }

  private async commandJson(commandArg: string, paramsArg: Record<string, string | number | boolean> = {}): Promise<Record<string, unknown>> {
    return this.getJson('command.cgi', { cmd: commandArg, ...paramsArg });
  }

  private async getJson(pathArg: string, paramsArg: Record<string, string | number | boolean | undefined> = {}): Promise<Record<string, unknown>> {
    const response = await this.request(pathArg, paramsArg, this.config.timeoutMs || agentDvrDefaultTimeoutMs);
    if (!response.ok) {
      throw new AgentDvrApiConnectionError(`Agent DVR HTTP ${response.status} for ${pathArg}.`);
    }
    const text = await response.text();
    const contentType = response.headers.get('content-type') || '';
    if (!contentType.toLowerCase().includes('application/json') && !text.trim().startsWith('{') && !text.trim().startsWith('[')) {
      throw new AgentDvrApiError(`Unexpected Agent DVR response content type: ${contentType || 'unknown'}.`);
    }
    const json = JSON.parse(text) as unknown;
    if (!json || typeof json !== 'object' || Array.isArray(json)) {
      throw new AgentDvrApiError('Unexpected Agent DVR JSON response shape.');
    }
    const record = json as Record<string, unknown>;
    if ('error' in record && record.error !== undefined && record.error !== null && record.error !== false && record.error !== '') {
      throw new AgentDvrApiError(`Agent DVR API error: ${String(record.error)}`);
    }
    return record;
  }

  private async request(pathArg: string, paramsArg: Record<string, string | number | boolean | undefined> = {}, timeoutMsArg: number): Promise<Response> {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), timeoutMsArg);
    try {
      return await fetch(this.urlString(pathArg, paramsArg), {
        method: 'GET',
        headers: {
          Accept: 'application/json, image/*, text/plain, */*',
          'User-Agent': 'SmarthomeExchangeAgentDVR/1.0',
        },
        signal: controller.signal,
      });
    } catch (errorArg) {
      throw new AgentDvrApiConnectionError(this.errorMessage(errorArg));
    } finally {
      clearTimeout(timer);
    }
  }

  private urlString(pathArg: string, paramsArg: Record<string, string | number | boolean | undefined> = {}): string {
    const url = new URL(pathArg, this.baseUrl());
    for (const [key, value] of Object.entries(paramsArg)) {
      if (value !== undefined) {
        url.searchParams.set(key, String(value));
      }
    }
    return url.toString();
  }

  private baseUrl(): string {
    const endpoint = this.endpoint();
    if (!endpoint.url) {
      throw new AgentDvrApiConnectionError('Agent DVR local HTTP endpoint is not configured.');
    }
    return endpoint.url;
  }

  private safeBaseUrl(): string | undefined {
    try {
      return this.baseUrl();
    } catch {
      return undefined;
    }
  }

  private endpoint(): { protocol: TAgentDvrProtocol; host?: string; port: number; url?: string } {
    const url = safeUrl(this.config.serverUrl || this.config.url || this.config.host);
    if (url) {
      const protocol: TAgentDvrProtocol = url.protocol === 'https:' ? 'https' : 'http';
      const port = url.port ? Number(url.port) : this.config.port || (protocol === 'https' ? 443 : agentDvrDefaultPort);
      url.protocol = `${protocol}:`;
      url.port = String(port);
      if (!url.pathname || url.pathname === '/') {
        url.pathname = '/';
      }
      url.search = '';
      url.hash = '';
      return { protocol, host: url.hostname, port, url: url.toString() };
    }
    const protocol = this.config.protocol || 'http';
    const port = this.config.port || agentDvrDefaultPort;
    const host = this.config.host;
    return { protocol, host, port, url: host ? `${protocol}://${host}:${port}/` : undefined };
  }

  private hasLiveTarget(): boolean {
    const endpoint = this.endpoint();
    return Boolean(endpoint.host && endpoint.url);
  }

  private hasManualData(): boolean {
    return Boolean(this.config.status || this.config.devices?.length || this.config.name || this.config.uniqueId || this.config.snapshot);
  }

  private profilesFromRaw(profilesArg?: Record<string, unknown>): IAgentDvrProfile[] | undefined {
    if (!profilesArg) {
      return undefined;
    }
    if (Array.isArray(profilesArg.profiles)) {
      return profilesArg.profiles as IAgentDvrProfile[];
    }
    return undefined;
  }

  private activeProfile(profilesArg: IAgentDvrProfile[]): string | null {
    return profilesArg.find((profileArg) => profileArg.active)?.name || null;
  }

  private locationName(locationsArg: IAgentDvrLocation[], indexArg: number | undefined): string | undefined {
    if (indexArg === undefined || indexArg < 0 || indexArg >= locationsArg.length) {
      return undefined;
    }
    return locationsArg[indexArg]?.name;
  }

  private findDevice(snapshotArg: IAgentDvrSnapshot, objectIdArg: number | undefined, objectTypeIdArg: number | undefined): IAgentDvrDeviceSnapshot | undefined {
    if (objectIdArg === undefined && objectTypeIdArg === undefined) {
      return snapshotArg.devices.find((deviceArg) => deviceArg.typeId === agentDvrCameraTypeId);
    }
    return snapshotArg.devices.find((deviceArg) => {
      if (objectIdArg !== undefined && deviceArg.objectId !== objectIdArg) {
        return false;
      }
      if (objectTypeIdArg !== undefined && deviceArg.typeId !== objectTypeIdArg) {
        return false;
      }
      return true;
    });
  }

  private responseIsFailure(responseArg: Record<string, unknown>): boolean {
    if (responseArg.success === false || responseArg.ok === false) {
      return true;
    }
    const result = responseArg.result;
    return typeof result === 'string' && result.toLowerCase() === 'error';
  }

  private cleanParams(paramsArg: Record<string, string | number | boolean | undefined>): Record<string, string | number | boolean> {
    return Object.fromEntries(Object.entries(paramsArg).filter(([, valueArg]) => valueArg !== undefined)) as Record<string, string | number | boolean>;
  }

  private isSnapshot(valueArg: unknown): valueArg is IAgentDvrSnapshot {
    return Boolean(valueArg && typeof valueArg === 'object' && 'server' in valueArg && 'devices' in valueArg);
  }

  private isDeviceSnapshot(valueArg: IAgentDvrDeviceSnapshot | IAgentDvrRawDevice): valueArg is IAgentDvrDeviceSnapshot {
    return Boolean(valueArg && typeof valueArg === 'object' && 'objectId' in valueArg && 'typeId' in valueArg && 'urls' in valueArg);
  }

  private cloneSnapshot(snapshotArg: IAgentDvrSnapshot): IAgentDvrSnapshot {
    return JSON.parse(JSON.stringify(snapshotArg)) as IAgentDvrSnapshot;
  }

  private errorMessage(errorArg: unknown): string {
    return errorArg instanceof Error ? errorArg.message : String(errorArg);
  }
}

const safeUrl = (valueArg: string | undefined): URL | undefined => {
  if (!valueArg) {
    return undefined;
  }
  try {
    const url = new URL(valueArg);
    if (url.protocol === 'http:' || url.protocol === 'https:') {
      return url;
    }
  } catch {
    // Try again below with an explicit local HTTP scheme.
  }
  try {
    return new URL(`http://${valueArg}`);
  } catch {
    return undefined;
  }
};

const stringValue = (valueArg: unknown): string | undefined => {
  if (typeof valueArg === 'string' && valueArg.trim()) {
    return valueArg.trim();
  }
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return String(valueArg);
  }
  return undefined;
};

const numberValue = (valueArg: unknown): number | undefined => {
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return valueArg;
  }
  if (typeof valueArg === 'string' && valueArg.trim()) {
    const parsed = Number(valueArg);
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
};

const booleanValue = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'string') {
    const normalized = valueArg.trim().toLowerCase();
    if (['true', '1', 'yes', 'on'].includes(normalized)) {
      return true;
    }
    if (['false', '0', 'no', 'off'].includes(normalized)) {
      return false;
    }
  }
  if (typeof valueArg === 'number') {
    return valueArg !== 0;
  }
  return undefined;
};
