import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { BaseIntegration } from '../../core/classes.baseintegration.js';
import type { IIntegrationEntity, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
import { VolumioClient } from './volumio.classes.client.js';
import { VolumioConfigFlow } from './volumio.classes.configflow.js';
import { createVolumioDiscoveryDescriptor } from './volumio.discovery.js';
import { VolumioMapper } from './volumio.mapper.js';
import type { IVolumioConfig } from './volumio.types.js';

export class VolumioIntegration extends BaseIntegration<IVolumioConfig> {
  public readonly domain = 'volumio';
  public readonly displayName = 'Volumio';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createVolumioDiscoveryDescriptor();
  public readonly configFlow = new VolumioConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/volumio',
    upstreamDomain: 'volumio',
    integrationType: 'device',
    iotClass: 'local_polling',
    requirements: ['pyvolumio==0.1.5'],
    dependencies: [],
    afterDependencies: [],
    codeowners: ['@OnFreund'],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/volumio',
    zeroconf: ['_Volumio._tcp.local.'],
  };

  public async setup(configArg: IVolumioConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new VolumioRuntime(new VolumioClient(configArg));
  }

  public async destroy(): Promise<void> {}
}

export class HomeAssistantVolumioIntegration extends VolumioIntegration {}

class VolumioRuntime implements IIntegrationRuntime {
  public domain = 'volumio';

  constructor(private readonly client: VolumioClient) {}

  public async devices(): Promise<shxInterfaces.data.IDeviceDefinition[]> {
    return VolumioMapper.toDevices(await this.client.getSnapshot());
  }

  public async entities(): Promise<IIntegrationEntity[]> {
    return VolumioMapper.toEntities(await this.client.getSnapshot());
  }

  public async subscribe(): Promise<() => Promise<void>> {
    return this.client.watchSocketIoPush();
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === 'media_player') {
        return await this.callMediaPlayerService(requestArg);
      }
      if (requestArg.domain === 'volumio') {
        return await this.callVolumioService(requestArg);
      }
      return { success: false, error: `Unsupported Volumio service domain: ${requestArg.domain}` };
    } catch (errorArg) {
      return { success: false, error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }

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

  private async callMediaPlayerService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'play' || requestArg.service === 'media_play') {
      await this.client.play();
      return { success: true };
    }
    if (requestArg.service === 'pause' || requestArg.service === 'media_pause') {
      await this.client.pause();
      return { success: true };
    }
    if (requestArg.service === 'play_pause' || requestArg.service === 'media_play_pause') {
      await this.client.toggle();
      return { success: true };
    }
    if (requestArg.service === 'stop' || requestArg.service === 'media_stop') {
      await this.client.stop();
      return { success: true };
    }
    if (requestArg.service === 'next_track' || requestArg.service === 'media_next_track') {
      await this.client.next();
      return { success: true };
    }
    if (requestArg.service === 'previous_track' || requestArg.service === 'media_previous_track') {
      await this.client.previous();
      return { success: true };
    }
    if (requestArg.service === 'volume_set') {
      const level = this.numberData(requestArg, 'volume_level');
      if (typeof level !== 'number') {
        return { success: false, error: 'Volumio volume_set requires data.volume_level.' };
      }
      await this.client.setVolumeLevel(level);
      return { success: true };
    }
    if (requestArg.service === 'volume_up') {
      await this.client.volumeUp();
      return { success: true };
    }
    if (requestArg.service === 'volume_down') {
      await this.client.volumeDown();
      return { success: true };
    }
    if (requestArg.service === 'volume_mute') {
      const muted = this.boolData(requestArg, 'is_volume_muted') ?? this.boolData(requestArg, 'muted') ?? this.boolData(requestArg, 'mute');
      if (typeof muted !== 'boolean') {
        return { success: false, error: 'Volumio volume_mute requires data.is_volume_muted.' };
      }
      await this.client.setMuted(muted);
      return { success: true };
    }
    if (requestArg.service === 'select_source') {
      const source = this.stringData(requestArg, 'source');
      if (!source) {
        return { success: false, error: 'Volumio select_source requires data.source.' };
      }
      await this.client.playPlaylist(source);
      return { success: true };
    }
    if (requestArg.service === 'media_seek' || requestArg.service === 'seek') {
      const position = this.numberData(requestArg, 'seek_position') ?? this.numberData(requestArg, 'position');
      if (typeof position !== 'number') {
        return { success: false, error: 'Volumio media_seek requires data.seek_position.' };
      }
      await this.client.seek(position);
      return { success: true };
    }
    if (requestArg.service === 'shuffle_set') {
      const shuffle = this.boolData(requestArg, 'shuffle');
      if (typeof shuffle !== 'boolean') {
        return { success: false, error: 'Volumio shuffle_set requires data.shuffle.' };
      }
      await this.client.setShuffle(shuffle);
      return { success: true };
    }
    if (requestArg.service === 'repeat_set') {
      const repeat = this.stringData(requestArg, 'repeat') || this.stringData(requestArg, 'repeat_mode');
      await this.client.repeatAll(repeat !== 'off');
      return { success: true };
    }
    if (requestArg.service === 'clear_playlist') {
      await this.client.clearPlaylist();
      return { success: true };
    }
    if (requestArg.service === 'play_media') {
      const mediaId = requestArg.data?.media_content_id ?? requestArg.data?.mediaId ?? requestArg.data?.item;
      if (mediaId === undefined) {
        return { success: false, error: 'Volumio play_media requires data.media_content_id.' };
      }
      await this.client.replaceAndPlay(this.mediaPayload(mediaId));
      return { success: true };
    }
    return { success: false, error: `Unsupported Volumio media_player service: ${requestArg.service}` };
  }

  private async callVolumioService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'command') {
      const command = this.stringData(requestArg, 'cmd') || this.stringData(requestArg, 'command');
      if (!command) {
        return { success: false, error: 'Volumio command service requires data.cmd.' };
      }
      const params = { ...requestArg.data };
      delete params.command;
      params.cmd = command;
      return { success: true, data: await this.client.sendCommand(params) };
    }
    if (requestArg.service === 'socketio_subscribe') {
      await this.client.watchSocketIoPush();
    }
    return { success: false, error: `Unsupported Volumio service: ${requestArg.service}` };
  }

  private mediaPayload(valueArg: unknown): unknown {
    if (typeof valueArg === 'string') {
      try {
        return JSON.parse(valueArg) as unknown;
      } catch {
        return { uri: valueArg };
      }
    }
    return valueArg;
  }

  private stringData(requestArg: IServiceCallRequest, keyArg: string): string | undefined {
    const value = requestArg.data?.[keyArg];
    return typeof value === 'string' && value.trim() ? value.trim() : undefined;
  }

  private numberData(requestArg: IServiceCallRequest, keyArg: string): number | undefined {
    const value = requestArg.data?.[keyArg];
    if (typeof value === 'number' && Number.isFinite(value)) {
      return value;
    }
    if (typeof value === 'string' && value.trim()) {
      const parsed = Number(value);
      return Number.isFinite(parsed) ? parsed : undefined;
    }
    return undefined;
  }

  private boolData(requestArg: IServiceCallRequest, keyArg: string): boolean | undefined {
    const value = requestArg.data?.[keyArg];
    if (typeof value === 'boolean') {
      return value;
    }
    if (typeof value === 'string') {
      if (value.toLowerCase() === 'true') {
        return true;
      }
      if (value.toLowerCase() === 'false') {
        return false;
      }
    }
    return undefined;
  }
}
