import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest } from '../../core/types.js';
import type {
  IPiHoleClientCommand,
  IPiHoleConfig,
  IPiHoleRawData,
  IPiHoleSnapshot,
  IPiHoleStatistics,
  IPiHoleVersions,
  TPiHoleApiVersion,
  TPiHoleBlockingStatus,
  TPiHoleSnapshotSource,
} from './pi_hole.types.js';
import { piHoleDefaultLocation, piHoleDefaultName, piHoleDefaultPort, piHoleDomain } from './pi_hole.types.js';

interface IPiHoleSnapshotOptions {
  config: IPiHoleConfig;
  rawData?: IPiHoleRawData;
  online?: boolean;
  source?: TPiHoleSnapshotSource;
  apiVersion?: TPiHoleApiVersion;
  error?: string;
}

interface IPiHoleStatDescription {
  key: keyof IPiHoleStatistics;
  entityKey: string;
  name: string;
  unit?: string;
  precision?: number;
}

const releaseBaseUrls = {
  core: 'https://github.com/pi-hole/pi-hole/releases/tag',
  web: 'https://github.com/pi-hole/AdminLTE/releases/tag',
  ftl: 'https://github.com/pi-hole/FTL/releases/tag',
};

const v5StatisticDescriptions: IPiHoleStatDescription[] = [
  { key: 'adsBlocked', entityKey: 'ads_blocked_today', name: 'Ads blocked today', unit: 'ads', precision: 0 },
  { key: 'adsPercentage', entityKey: 'ads_percentage_today', name: 'Ads percentage blocked today', unit: '%', precision: 1 },
  { key: 'clientsSeen', entityKey: 'clients_ever_seen', name: 'Seen clients', unit: 'clients', precision: 0 },
  { key: 'dnsQueries', entityKey: 'dns_queries_today', name: 'DNS queries today', unit: 'queries', precision: 0 },
  { key: 'domainsBlocked', entityKey: 'domains_being_blocked', name: 'Domains blocked', unit: 'domains', precision: 0 },
  { key: 'queriesCached', entityKey: 'queries_cached', name: 'DNS queries cached', unit: 'queries', precision: 0 },
  { key: 'queriesForwarded', entityKey: 'queries_forwarded', name: 'DNS queries forwarded', unit: 'queries', precision: 0 },
  { key: 'uniqueClients', entityKey: 'unique_clients', name: 'DNS unique clients', unit: 'clients', precision: 0 },
  { key: 'uniqueDomains', entityKey: 'unique_domains', name: 'DNS unique domains', unit: 'domains', precision: 0 },
];

const v6StatisticDescriptions: IPiHoleStatDescription[] = [
  { key: 'adsBlocked', entityKey: 'ads_blocked', name: 'Ads blocked', unit: 'ads', precision: 0 },
  { key: 'adsPercentage', entityKey: 'percent_ads_blocked', name: 'Ads percentage blocked', unit: '%', precision: 2 },
  { key: 'clientsSeen', entityKey: 'clients_ever_seen', name: 'Seen clients', unit: 'clients', precision: 0 },
  { key: 'dnsQueries', entityKey: 'dns_queries', name: 'DNS queries', unit: 'queries', precision: 0 },
  { key: 'domainsBlocked', entityKey: 'domains_being_blocked', name: 'Domains blocked', unit: 'domains', precision: 0 },
  { key: 'queriesCached', entityKey: 'queries_cached', name: 'DNS queries cached', unit: 'queries', precision: 0 },
  { key: 'queriesForwarded', entityKey: 'queries_forwarded', name: 'DNS queries forwarded', unit: 'queries', precision: 0 },
  { key: 'uniqueClients', entityKey: 'unique_clients', name: 'DNS unique clients', unit: 'clients', precision: 0 },
  { key: 'uniqueDomains', entityKey: 'unique_domains', name: 'DNS unique domains', unit: 'domains', precision: 0 },
];

const updateDescriptions: Array<{ key: keyof IPiHoleVersions; name: string; title: string }> = [
  { key: 'core', name: 'Core update available', title: 'Pi-hole Core' },
  { key: 'web', name: 'Web update available', title: 'Pi-hole Web interface' },
  { key: 'ftl', name: 'FTL update available', title: 'Pi-hole FTL DNS' },
];

export class PiHoleMapper {
  public static toSnapshot(optionsArg: IPiHoleSnapshotOptions): IPiHoleSnapshot {
    if (optionsArg.config.snapshot && !optionsArg.rawData) {
      return this.normalizeSnapshot(optionsArg.config.snapshot, optionsArg.config, optionsArg.source || 'snapshot');
    }

    const rawData = this.rawData(optionsArg.config, optionsArg.rawData);
    const apiVersion = optionsArg.apiVersion || optionsArg.config.apiVersion || this.versionFromRaw(rawData);
    const statistics = this.statisticsFromRaw(rawData, optionsArg.config.statistics);
    const versions = this.versionsFromRaw(rawData, optionsArg.config.versions);
    const status = this.statusFromRaw(rawData, optionsArg.config.status);
    const online = optionsArg.online ?? optionsArg.config.online ?? Boolean(optionsArg.rawData || optionsArg.config.rawData || optionsArg.config.v5Summary || optionsArg.config.v6Summary || optionsArg.config.status || optionsArg.config.statistics || optionsArg.config.versions);

    return {
      online,
      apiVersion,
      status,
      statistics,
      versions,
      raw: rawData,
      host: optionsArg.config.host,
      port: optionsArg.config.port || (optionsArg.config.host ? this.defaultPort(optionsArg.config.ssl) : undefined),
      ssl: optionsArg.config.ssl ?? false,
      verifySsl: optionsArg.config.verifySsl ?? true,
      location: optionsArg.config.location || piHoleDefaultLocation,
      name: optionsArg.config.name || piHoleDefaultName,
      uniqueId: optionsArg.config.uniqueId,
      updatedAt: new Date().toISOString(),
      source: optionsArg.source || (Object.keys(rawData).length ? 'manual' : 'runtime'),
      error: optionsArg.error,
    };
  }

  public static toDevices(snapshotArg: IPiHoleSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const deviceId = this.deviceId(snapshotArg);
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      { id: 'blocking', capability: 'switch', name: 'DNS blocking', readable: true, writable: true },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'connectivity', value: snapshotArg.online ? 'online' : 'offline', updatedAt },
      { featureId: 'blocking', value: snapshotArg.status === 'enabled', updatedAt },
    ];

    for (const description of this.statisticDescriptions(snapshotArg)) {
      const value = this.statisticValue(snapshotArg.statistics, description.key, description.precision);
      features.push({ id: description.entityKey, capability: 'sensor', name: description.name, readable: true, writable: false, unit: description.unit });
      state.push({ featureId: description.entityKey, value, updatedAt });
    }

    for (const description of updateDescriptions) {
      const update = snapshotArg.versions[description.key];
      features.push({ id: `${description.key}_update_available`, capability: 'sensor', name: description.name, readable: true, writable: false });
      state.push({ featureId: `${description.key}_update_available`, value: Boolean(update.updateAvailable), updatedAt });
    }

    return [{
      id: deviceId,
      integrationDomain: piHoleDomain,
      name: this.deviceName(snapshotArg),
      protocol: snapshotArg.host ? 'http' : 'unknown',
      manufacturer: 'Pi-hole',
      model: snapshotArg.apiVersion ? `Pi-hole API v${snapshotArg.apiVersion}` : 'Pi-hole',
      online: snapshotArg.online,
      features,
      state,
      metadata: this.cleanAttributes({
        host: snapshotArg.host,
        port: snapshotArg.port,
        ssl: snapshotArg.ssl,
        location: snapshotArg.location,
        apiVersion: snapshotArg.apiVersion,
        source: snapshotArg.source,
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IPiHoleSnapshot): IIntegrationEntity[] {
    const deviceId = this.deviceId(snapshotArg);
    const baseName = this.deviceName(snapshotArg);
    const baseSlug = this.slug(baseName);
    const uniqueBase = this.uniqueBase(snapshotArg);
    const entities: IIntegrationEntity[] = [
      this.entity('binary_sensor', `${baseName} Status`, deviceId, `${uniqueBase}_status`, this.statusState(snapshotArg.status), snapshotArg.online, {
        deviceClass: 'running',
        piHoleStatus: snapshotArg.status,
        apiVersion: snapshotArg.apiVersion,
      }),
      {
        id: `switch.${baseSlug}`,
        uniqueId: `${piHoleDomain}_${uniqueBase}_switch`,
        integrationDomain: piHoleDomain,
        deviceId,
        platform: 'switch',
        name: baseName,
        state: this.statusState(snapshotArg.status),
        attributes: this.cleanAttributes({
          piHoleSwitch: 'blocking',
          writable: true,
          apiVersion: snapshotArg.apiVersion,
        }),
        available: snapshotArg.online && snapshotArg.status !== 'unknown',
      },
    ];

    for (const description of this.statisticDescriptions(snapshotArg)) {
      const value = this.statisticValue(snapshotArg.statistics, description.key, description.precision);
      entities.push(this.entity('sensor', `${baseName} ${description.name}`, deviceId, `${uniqueBase}_sensor_${description.entityKey}`, value, snapshotArg.online && value !== null, {
        unit: description.unit,
        stateClass: typeof value === 'number' ? 'measurement' : undefined,
        apiVersion: snapshotArg.apiVersion,
      }));
    }

    for (const description of updateDescriptions) {
      const update = snapshotArg.versions[description.key];
      const latestVersion = update.updateAvailable ? update.latest : update.current || update.latest;
      entities.push(this.entity('update', `${baseName} ${description.name}`, deviceId, `${uniqueBase}_update_${description.key}`, update.updateAvailable ? 'on' : 'off', snapshotArg.online && Boolean(update.current || update.latest), {
        title: description.title,
        entityCategory: 'diagnostic',
        installedVersion: update.current,
        latestVersion,
        releaseUrl: latestVersion ? `${releaseBaseUrls[description.key]}/${latestVersion}` : undefined,
      }));
    }

    return entities;
  }

  public static commandForService(snapshotArg: IPiHoleSnapshot, requestArg: IServiceCallRequest): IPiHoleClientCommand | { error: string } | undefined {
    if (requestArg.domain === piHoleDomain) {
      if (requestArg.service === 'refresh') {
        return this.command(snapshotArg, 'refresh', requestArg, undefined);
      }
      if (requestArg.service === 'enable' || requestArg.service === 'disable') {
        return this.command(snapshotArg, requestArg.service, requestArg, undefined);
      }
      return undefined;
    }

    if (requestArg.domain === 'switch' && (requestArg.service === 'turn_on' || requestArg.service === 'turn_off')) {
      const target = this.targetSwitch(snapshotArg, requestArg);
      if ('error' in target) {
        return target;
      }
      return this.command(snapshotArg, requestArg.service === 'turn_on' ? 'enable' : 'disable', requestArg, target.entity);
    }

    return undefined;
  }

  public static deviceId(snapshotArg: IPiHoleSnapshot): string {
    return `${piHoleDomain}.service.${this.uniqueBase(snapshotArg)}`;
  }

  public static slug(valueArg: string): string {
    return valueArg.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'pi_hole';
  }

  private static normalizeSnapshot(snapshotArg: IPiHoleSnapshot, configArg: IPiHoleConfig, sourceArg: TPiHoleSnapshotSource): IPiHoleSnapshot {
    const raw = this.rawData(configArg, snapshotArg.raw);
    return {
      ...snapshotArg,
      online: snapshotArg.online,
      apiVersion: snapshotArg.apiVersion || configArg.apiVersion || this.versionFromRaw(raw),
      status: snapshotArg.status || this.statusFromRaw(raw, configArg.status),
      statistics: this.completeStatistics(snapshotArg.statistics || this.statisticsFromRaw(raw, configArg.statistics)),
      versions: this.completeVersions(snapshotArg.versions || this.versionsFromRaw(raw, configArg.versions)),
      raw,
      host: snapshotArg.host || configArg.host,
      port: snapshotArg.port || configArg.port || (snapshotArg.host || configArg.host ? this.defaultPort(snapshotArg.ssl ?? configArg.ssl) : undefined),
      ssl: snapshotArg.ssl ?? configArg.ssl ?? false,
      verifySsl: snapshotArg.verifySsl ?? configArg.verifySsl ?? true,
      location: snapshotArg.location || configArg.location || piHoleDefaultLocation,
      name: snapshotArg.name || configArg.name || piHoleDefaultName,
      uniqueId: snapshotArg.uniqueId || configArg.uniqueId,
      updatedAt: snapshotArg.updatedAt || new Date().toISOString(),
      source: snapshotArg.source || sourceArg,
      error: snapshotArg.error,
    };
  }

  private static rawData(configArg: IPiHoleConfig, rawDataArg?: IPiHoleRawData): IPiHoleRawData {
    return this.cleanAttributes({
      ...(configArg.rawData || {}),
      ...(rawDataArg || {}),
      v5Summary: rawDataArg?.v5Summary || configArg.v5Summary || configArg.rawData?.v5Summary,
      v5Versions: rawDataArg?.v5Versions || configArg.v5Versions || configArg.rawData?.v5Versions,
      v6Summary: rawDataArg?.v6Summary || configArg.v6Summary || configArg.rawData?.v6Summary,
      v6Blocking: rawDataArg?.v6Blocking || configArg.v6Blocking || configArg.rawData?.v6Blocking,
      v6Versions: rawDataArg?.v6Versions || configArg.v6Versions || configArg.rawData?.v6Versions,
    }) as IPiHoleRawData;
  }

  private static statisticsFromRaw(rawDataArg: IPiHoleRawData, overrideArg?: Partial<IPiHoleStatistics>): IPiHoleStatistics {
    const queries = this.recordValue(rawDataArg.v6Summary?.queries);
    const clients = this.recordValue(rawDataArg.v6Summary?.clients);
    const gravity = this.recordValue(rawDataArg.v6Summary?.gravity);
    const statistics = this.completeStatistics({
      adsBlocked: this.numberValue(rawDataArg.v5Summary?.ads_blocked_today) ?? this.numberValue(queries?.blocked),
      adsPercentage: this.numberValue(rawDataArg.v5Summary?.ads_percentage_today) ?? this.numberValue(queries?.percent_blocked),
      clientsSeen: this.numberValue(rawDataArg.v5Summary?.clients_ever_seen) ?? this.numberValue(clients?.total),
      dnsQueries: this.numberValue(rawDataArg.v5Summary?.dns_queries_today) ?? this.numberValue(queries?.total),
      domainsBlocked: this.numberValue(rawDataArg.v5Summary?.domains_being_blocked) ?? this.numberValue(gravity?.domains_being_blocked),
      queriesCached: this.numberValue(rawDataArg.v5Summary?.queries_cached) ?? this.numberValue(queries?.cached),
      queriesForwarded: this.numberValue(rawDataArg.v5Summary?.queries_forwarded) ?? this.numberValue(queries?.forwarded),
      uniqueClients: this.numberValue(rawDataArg.v5Summary?.unique_clients) ?? this.numberValue(clients?.active),
      uniqueDomains: this.numberValue(rawDataArg.v5Summary?.unique_domains) ?? this.numberValue(queries?.unique_domains),
    });
    return this.completeStatistics({ ...statistics, ...(overrideArg || {}) });
  }

  private static versionsFromRaw(rawDataArg: IPiHoleRawData, overrideArg?: Partial<IPiHoleVersions>): IPiHoleVersions {
    const versionRoot = rawDataArg.v6Versions?.version;
    const versions = this.completeVersions({
      core: {
        current: this.stringValue(rawDataArg.v5Versions?.core_current) || this.stringValue(versionRoot?.core?.local?.version),
        latest: this.stringValue(rawDataArg.v5Versions?.core_latest) || this.stringValue(versionRoot?.core?.remote?.version),
        updateAvailable: this.booleanValue(rawDataArg.v5Versions?.core_update) ?? this.updateAvailable(versionRoot?.core),
      },
      web: {
        current: this.stringValue(rawDataArg.v5Versions?.web_current) || this.stringValue(versionRoot?.web?.local?.version),
        latest: this.stringValue(rawDataArg.v5Versions?.web_latest) || this.stringValue(versionRoot?.web?.remote?.version),
        updateAvailable: this.booleanValue(rawDataArg.v5Versions?.web_update) ?? this.updateAvailable(versionRoot?.web),
      },
      ftl: {
        current: this.stringValue(rawDataArg.v5Versions?.FTL_current) || this.stringValue(versionRoot?.ftl?.local?.version),
        latest: this.stringValue(rawDataArg.v5Versions?.FTL_latest) || this.stringValue(versionRoot?.ftl?.remote?.version),
        updateAvailable: this.booleanValue(rawDataArg.v5Versions?.FTL_update) ?? this.updateAvailable(versionRoot?.ftl),
      },
    });

    return this.completeVersions({
      core: { ...versions.core, ...(overrideArg?.core || {}) },
      web: { ...versions.web, ...(overrideArg?.web || {}) },
      ftl: { ...versions.ftl, ...(overrideArg?.ftl || {}) },
    });
  }

  private static statusFromRaw(rawDataArg: IPiHoleRawData, overrideArg?: string | boolean): TPiHoleBlockingStatus {
    return this.normalizeStatus(overrideArg ?? rawDataArg.v5Summary?.status ?? rawDataArg.v6Blocking?.blocking);
  }

  private static versionFromRaw(rawDataArg: IPiHoleRawData): TPiHoleApiVersion | undefined {
    if (rawDataArg.v6Summary || rawDataArg.v6Blocking || rawDataArg.v6Versions) return 6;
    if (rawDataArg.v5Summary || rawDataArg.v5Versions) return 5;
    return undefined;
  }

  private static command(snapshotArg: IPiHoleSnapshot, serviceArg: 'enable' | 'disable' | 'refresh', requestArg: IServiceCallRequest, entityArg: IIntegrationEntity | undefined): IPiHoleClientCommand | { error: string } {
    if (serviceArg === 'refresh') {
      return {
        type: 'refresh',
        service: requestArg.service,
        target: requestArg.target,
        entityId: entityArg?.id || requestArg.target.entityId,
        deviceId: entityArg?.deviceId || requestArg.target.deviceId,
        uniqueId: entityArg?.uniqueId,
        apiVersion: snapshotArg.apiVersion,
      };
    }

    const durationSeconds = serviceArg === 'disable' ? this.durationSeconds(requestArg.data?.duration) : undefined;
    if (durationSeconds === null) {
      return { error: 'Pi-hole disable requires data.duration as seconds or HH:MM:SS when provided.' };
    }

    const apiVersion = snapshotArg.apiVersion || 6;
    const enabled = serviceArg === 'enable';
    return this.cleanAttributes({
      type: serviceArg,
      service: requestArg.service,
      method: apiVersion === 6 ? 'POST' : 'GET',
      path: apiVersion === 6 ? '/api/dns/blocking' : `/${snapshotArg.location || piHoleDefaultLocation}/api.php`,
      query: apiVersion === 5 ? enabled ? { enable: 'True' } : { disable: durationSeconds ?? true } : undefined,
      payload: apiVersion === 6 ? { blocking: enabled, timer: enabled ? null : durationSeconds ?? null } : undefined,
      target: requestArg.target,
      entityId: entityArg?.id || requestArg.target.entityId,
      deviceId: entityArg?.deviceId || requestArg.target.deviceId,
      uniqueId: entityArg?.uniqueId,
      apiVersion,
      enabled,
      durationSeconds,
      requiresAuth: true,
    }) as IPiHoleClientCommand;
  }

  private static targetSwitch(snapshotArg: IPiHoleSnapshot, requestArg: IServiceCallRequest): { entity?: IIntegrationEntity } | { error: string } {
    const entity = this.findTargetEntity(snapshotArg, requestArg);
    if (entity?.attributes?.piHoleSwitch === 'blocking') {
      return { entity };
    }
    if (requestArg.target.deviceId && requestArg.target.deviceId === this.deviceId(snapshotArg)) {
      return { entity };
    }
    return { error: 'Pi-hole switch service calls require the Pi-hole switch entity or device target.' };
  }

  private static findTargetEntity(snapshotArg: IPiHoleSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    if (!requestArg.target.entityId) {
      return undefined;
    }
    return this.toEntities(snapshotArg).find((entityArg) => entityArg.id === requestArg.target.entityId);
  }

  private static statisticDescriptions(snapshotArg: IPiHoleSnapshot): IPiHoleStatDescription[] {
    return snapshotArg.apiVersion === 6 ? v6StatisticDescriptions : v5StatisticDescriptions;
  }

  private static statisticValue(statisticsArg: IPiHoleStatistics, keyArg: keyof IPiHoleStatistics, precisionArg = 2): number | null {
    const value = statisticsArg[keyArg];
    if (typeof value !== 'number' || !Number.isFinite(value)) {
      return null;
    }
    const factor = 10 ** precisionArg;
    return Math.round(value * factor) / factor;
  }

  private static entity(platformArg: IIntegrationEntity['platform'], nameArg: string, deviceIdArg: string, uniqueIdArg: string, stateArg: unknown, availableArg: boolean, attributesArg: Record<string, unknown> = {}): IIntegrationEntity {
    return {
      id: `${platformArg}.${this.slug(nameArg)}`,
      uniqueId: `${piHoleDomain}_${uniqueIdArg}`,
      integrationDomain: piHoleDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: stateArg,
      attributes: this.cleanAttributes(attributesArg),
      available: availableArg,
    };
  }

  private static statusState(statusArg: TPiHoleBlockingStatus): 'on' | 'off' | 'unknown' {
    if (statusArg === 'enabled') return 'on';
    if (statusArg === 'disabled') return 'off';
    return 'unknown';
  }

  private static normalizeStatus(valueArg: unknown): TPiHoleBlockingStatus {
    if (valueArg === true) return 'enabled';
    if (valueArg === false) return 'disabled';
    const value = typeof valueArg === 'string' ? valueArg.toLowerCase() : '';
    if (value === 'enabled' || value === 'enable' || value === 'true') return 'enabled';
    if (value === 'disabled' || value === 'disable' || value === 'false') return 'disabled';
    return 'unknown';
  }

  private static completeStatistics(valueArg: Partial<IPiHoleStatistics>): IPiHoleStatistics {
    return {
      adsBlocked: this.numberOrNull(valueArg.adsBlocked),
      adsPercentage: this.numberOrNull(valueArg.adsPercentage),
      clientsSeen: this.numberOrNull(valueArg.clientsSeen),
      dnsQueries: this.numberOrNull(valueArg.dnsQueries),
      domainsBlocked: this.numberOrNull(valueArg.domainsBlocked),
      queriesCached: this.numberOrNull(valueArg.queriesCached),
      queriesForwarded: this.numberOrNull(valueArg.queriesForwarded),
      uniqueClients: this.numberOrNull(valueArg.uniqueClients),
      uniqueDomains: this.numberOrNull(valueArg.uniqueDomains),
    };
  }

  private static completeVersions(valueArg: Partial<IPiHoleVersions>): IPiHoleVersions {
    return {
      core: { ...(valueArg.core || {}) },
      web: { ...(valueArg.web || {}) },
      ftl: { ...(valueArg.ftl || {}) },
    };
  }

  private static updateAvailable(componentArg: { local?: { hash?: string; version?: string }; remote?: { hash?: string; version?: string } } | undefined): boolean | undefined {
    if (!componentArg) return undefined;
    const localHash = this.stringValue(componentArg.local?.hash);
    const remoteHash = this.stringValue(componentArg.remote?.hash);
    if (localHash && remoteHash) return localHash !== remoteHash;
    const localVersion = this.stringValue(componentArg.local?.version);
    const remoteVersion = this.stringValue(componentArg.remote?.version);
    return localVersion && remoteVersion ? localVersion !== remoteVersion : undefined;
  }

  private static durationSeconds(valueArg: unknown): number | undefined | null {
    if (valueArg === undefined || valueArg === null || valueArg === '') {
      return undefined;
    }
    if (typeof valueArg === 'number' && Number.isFinite(valueArg) && valueArg >= 0) {
      return Math.round(valueArg);
    }
    if (typeof valueArg !== 'string') {
      return null;
    }
    const trimmed = valueArg.trim();
    if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
      return Math.round(Number(trimmed));
    }
    const parts = trimmed.split(':').map((partArg) => Number(partArg));
    if (parts.length < 2 || parts.length > 3 || parts.some((partArg) => !Number.isInteger(partArg) || partArg < 0)) {
      return null;
    }
    const [hours, minutes, seconds] = parts.length === 3 ? parts : [0, parts[0], parts[1]];
    if (minutes > 59 || seconds > 59) {
      return null;
    }
    return hours * 3600 + minutes * 60 + seconds;
  }

  private static deviceName(snapshotArg: IPiHoleSnapshot): string {
    return snapshotArg.name || piHoleDefaultName;
  }

  private static uniqueBase(snapshotArg: IPiHoleSnapshot): string {
    return this.slug(snapshotArg.uniqueId || snapshotArg.host && `${snapshotArg.host}_${snapshotArg.port || ''}` || this.deviceName(snapshotArg));
  }

  private static defaultPort(sslArg: boolean | undefined): number {
    return sslArg ? 443 : piHoleDefaultPort;
  }

  private static recordValue(valueArg: unknown): Record<string, unknown> | undefined {
    return typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
  }

  private static numberValue(valueArg: unknown): number | undefined {
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) return valueArg;
    if (typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg))) return Number(valueArg);
    return undefined;
  }

  private static numberOrNull(valueArg: unknown): number | null {
    return this.numberValue(valueArg) ?? null;
  }

  private static stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg ? valueArg : undefined;
  }

  private static booleanValue(valueArg: unknown): boolean | undefined {
    return typeof valueArg === 'boolean' ? valueArg : undefined;
  }

  private static cleanAttributes<T extends Record<string, unknown>>(attributesArg: T): T {
    return Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined)) as T;
  }
}
