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 { YeelightClient } from './yeelight.classes.client.js';
import { YeelightConfigFlow } from './yeelight.classes.configflow.js';
import { createYeelightDiscoveryDescriptor } from './yeelight.discovery.js';
import { YeelightMapper } from './yeelight.mapper.js';
import type { IYeelightConfig, IYeelightLightStatePatch } from './yeelight.types.js';

export class YeelightIntegration extends BaseIntegration<IYeelightConfig> {
  public readonly domain = 'yeelight';
  public readonly displayName = 'Yeelight';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createYeelightDiscoveryDescriptor();
  public readonly configFlow = new YeelightConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/yeelight',
    upstreamDomain: 'yeelight',
    integrationType: 'device',
    iotClass: 'local_push',
    documentation: 'https://www.home-assistant.io/integrations/yeelight',
    requirements: ['yeelight==0.7.16', 'async-upnp-client==0.46.2'],
    dependencies: ['network'],
    afterDependencies: ['ssdp'],
    codeowners: ['@zewelor', '@shenxn', '@starkillerOG', '@alexyao2015'],
  };

  public async setup(configArg: IYeelightConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new YeelightRuntime(new YeelightClient(configArg));
  }

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

export class HomeAssistantYeelightIntegration extends YeelightIntegration {}

class YeelightRuntime implements IIntegrationRuntime {
  public domain = 'yeelight';

  constructor(private readonly client: YeelightClient) {}

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

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

  public async subscribe(handlerArg: (eventArg: ReturnType<typeof YeelightMapper.toIntegrationEvent>) => void): Promise<() => Promise<void>> {
    const unsubscribe = this.client.onEvent((eventArg) => handlerArg(YeelightMapper.toIntegrationEvent(eventArg)));
    return async () => unsubscribe();
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (!['light', 'number'].includes(requestArg.domain)) {
      return { success: false, error: `Unsupported Yeelight service domain: ${requestArg.domain}` };
    }
    const patch = this.patchForService(requestArg);
    if (!patch) {
      return { success: false, error: `Unsupported Yeelight service: ${requestArg.service}` };
    }
    await this.client.setLightState(patch);
    return { success: true };
  }

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

  private patchForService(requestArg: IServiceCallRequest): IYeelightLightStatePatch | undefined {
    if (requestArg.service === 'turn_on') {
      return { on: true, ...this.patchFromData(requestArg.data) };
    }
    if (requestArg.service === 'turn_off') {
      return { on: false, transition: this.numberValue(requestArg.data?.transition) };
    }
    if (requestArg.service === 'set_brightness' || requestArg.service === 'set_percentage') {
      const value = this.numberValue(requestArg.data?.brightness ?? requestArg.data?.percentage ?? requestArg.data?.value);
      return value === undefined ? undefined : { percentage: value, transition: this.numberValue(requestArg.data?.transition) };
    }
    if (requestArg.service === 'set_value') {
      const property = String(requestArg.data?.property || requestArg.data?.attribute || '').toLowerCase();
      const value = requestArg.data?.value;
      if (property === 'rgb' || property === 'rgb_color') {
        const rgb = this.rgbValue(value);
        return rgb ? { rgbColor: rgb, transition: this.numberValue(requestArg.data?.transition) } : undefined;
      }
      if (property === 'ct' || property === 'color_temp' || property === 'color_temp_kelvin' || property === 'kelvin') {
        const kelvin = this.numberValue(value);
        return kelvin === undefined ? undefined : { colorTempKelvin: kelvin, transition: this.numberValue(requestArg.data?.transition) };
      }
      const numeric = this.numberValue(value);
      return numeric === undefined ? undefined : { percentage: numeric, transition: this.numberValue(requestArg.data?.transition) };
    }
    return undefined;
  }

  private patchFromData(dataArg?: Record<string, unknown>): IYeelightLightStatePatch {
    const rgb = this.rgbValue(dataArg?.rgbColor ?? dataArg?.rgb_color);
    return {
      brightness: this.numberValue(dataArg?.brightness),
      percentage: this.numberValue(dataArg?.percentage),
      colorTempKelvin: this.numberValue(dataArg?.colorTempKelvin ?? dataArg?.color_temp_kelvin ?? dataArg?.kelvin),
      rgbColor: rgb,
      transition: this.numberValue(dataArg?.transition),
    };
  }

  private rgbValue(valueArg: unknown): [number, number, number] | undefined {
    if (!Array.isArray(valueArg) || valueArg.length < 3) {
      return undefined;
    }
    const red = this.numberValue(valueArg[0]);
    const green = this.numberValue(valueArg[1]);
    const blue = this.numberValue(valueArg[2]);
    return red === undefined || green === undefined || blue === undefined ? undefined : [red, green, blue];
  }

  private 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;
  }
}
