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 { NanoleafClient } from './nanoleaf.classes.client.js';
import { NanoleafConfigFlow } from './nanoleaf.classes.configflow.js';
import { createNanoleafDiscoveryDescriptor } from './nanoleaf.discovery.js';
import { NanoleafMapper } from './nanoleaf.mapper.js';
import type { INanoleafConfig } from './nanoleaf.types.js';

export class NanoleafIntegration extends BaseIntegration<INanoleafConfig> {
  public readonly domain = 'nanoleaf';
  public readonly displayName = 'Nanoleaf';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createNanoleafDiscoveryDescriptor();
  public readonly configFlow = new NanoleafConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/nanoleaf',
    upstreamDomain: 'nanoleaf',
    integrationType: 'device',
    iotClass: 'local_push',
    documentation: 'https://www.home-assistant.io/integrations/nanoleaf',
    requirements: ['aionanoleaf2==1.0.2'],
    codeowners: ['@milanmeu', '@joostlek', '@loebi-ch', '@JaspervRijbroek', '@jonathanrobichaud4'],
    zeroconf: ['_nanoleafms._tcp.local.', '_nanoleafapi._tcp.local.'],
    ssdp: ['Nanoleaf_aurora:light', 'nanoleaf:nl29', 'nanoleaf:nl42', 'nanoleaf:nl52', 'nanoleaf:nl69', 'inanoleaf:nl81'],
    homekitModels: ['NL29', 'NL42', 'NL47', 'NL48', 'NL52', 'NL59', 'NL69', 'NL81'],
  };

  public async setup(configArg: INanoleafConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new NanoleafRuntime(new NanoleafClient(configArg));
  }

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

export class HomeAssistantNanoleafIntegration extends NanoleafIntegration {}

class NanoleafRuntime implements IIntegrationRuntime {
  public domain = 'nanoleaf';

  constructor(private readonly client: NanoleafClient) {}

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

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

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === 'nanoleaf' && requestArg.service === 'create_auth_token') {
        const result = await this.client.createAuthToken();
        return { success: result.success, error: result.error, data: result };
      }

      if (requestArg.domain === 'button') {
        if (requestArg.service === 'press') {
          await this.client.identify();
          return { success: true };
        }
        return { success: false, error: `Unsupported Nanoleaf button service: ${requestArg.service}` };
      }

      if (requestArg.domain === 'select') {
        return this.handleSelectEffect(requestArg);
      }

      if (requestArg.domain === 'number') {
        return this.handleNumberService(requestArg);
      }

      if (requestArg.domain !== 'light') {
        return { success: false, error: `Unsupported Nanoleaf service domain: ${requestArg.domain}` };
      }

      if (requestArg.service === 'turn_on') {
        return this.handleTurnOn(requestArg);
      }
      if (requestArg.service === 'turn_off') {
        await this.client.turnOff(this.numberValue(requestArg.data, 'transition'));
        return { success: true };
      }
      if (requestArg.service === 'set_value') {
        return this.handleSetValue(requestArg);
      }
      if (requestArg.service === 'set_percentage' || requestArg.service === 'set_brightness') {
        return this.handleSetBrightness(requestArg);
      }
      if (requestArg.service === 'select_effect') {
        return this.handleSelectEffect(requestArg);
      }

      return { success: false, error: `Unsupported Nanoleaf light service: ${requestArg.service}` };
    } catch (error) {
      return { success: false, error: error instanceof Error ? error.message : String(error) };
    }
  }

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

  private async handleTurnOn(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    const effect = this.stringValue(requestArg.data, 'effect', 'option');
    if (effect) {
      await this.client.setEffect(effect);
    }

    const hsColor = requestArg.data?.hs_color;
    if (Array.isArray(hsColor) && typeof hsColor[0] === 'number' && typeof hsColor[1] === 'number') {
      await this.client.setHue(hsColor[0]);
      await this.client.setSaturation(hsColor[1]);
    }

    const colorTemperature = this.numberValue(requestArg.data, 'color_temp_kelvin', 'kelvin', 'ct');
    if (colorTemperature !== undefined) {
      await this.client.setColorTemperature(colorTemperature);
    }

    await this.client.turnOn();

    const brightness = this.brightnessPercent(requestArg.data);
    if (brightness !== undefined) {
      await this.client.setBrightness(brightness, this.numberValue(requestArg.data, 'transition'));
    }

    return { success: true };
  }

  private async handleNumberService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'set_value' && requestArg.service !== 'set_percentage' && requestArg.service !== 'set_brightness') {
      return { success: false, error: `Unsupported Nanoleaf number service: ${requestArg.service}` };
    }
    return requestArg.service === 'set_value' ? this.handleSetValue(requestArg) : this.handleSetBrightness(requestArg);
  }

  private async handleSetValue(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    const value = this.numberValue(requestArg.data, 'value', 'brightness', 'percentage');
    if (value === undefined) {
      return { success: false, error: 'Nanoleaf set_value requires data.value.' };
    }

    const target = `${requestArg.target.entityId ?? ''} ${this.stringValue(requestArg.data, 'attribute', 'feature') ?? ''}`.toLowerCase();
    if (target.includes('hue')) {
      await this.client.setHue(value);
      return { success: true };
    }
    if (target.includes('saturation') || target.includes('sat')) {
      await this.client.setSaturation(value);
      return { success: true };
    }
    if (target.includes('temperature') || target.includes('color_temp') || target.includes('ct')) {
      await this.client.setColorTemperature(value);
      return { success: true };
    }

    await this.client.setBrightness(this.brightnessPercent(requestArg.data) ?? value, this.numberValue(requestArg.data, 'transition'));
    return { success: true };
  }

  private async handleSetBrightness(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    const brightness = this.brightnessPercent(requestArg.data);
    if (brightness === undefined) {
      return { success: false, error: 'Nanoleaf brightness service requires data.percentage, data.brightness, or data.value.' };
    }
    await this.client.setBrightness(brightness, this.numberValue(requestArg.data, 'transition'));
    return { success: true };
  }

  private async handleSelectEffect(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    const effect = this.stringValue(requestArg.data, 'effect', 'option', 'value');
    if (!effect) {
      return { success: false, error: 'Nanoleaf select_effect requires data.effect or data.option.' };
    }
    await this.client.setEffect(effect);
    return { success: true };
  }

  private brightnessPercent(dataArg: Record<string, unknown> | undefined): number | undefined {
    const percentage = this.numberValue(dataArg, 'brightness_pct', 'percentage', 'percent', 'value');
    if (percentage !== undefined) {
      return this.clamp(Math.round(percentage), 0, 100);
    }
    const brightness = this.numberValue(dataArg, 'brightness');
    if (brightness === undefined) {
      return undefined;
    }
    return this.clamp(Math.round(brightness > 100 ? brightness / 2.55 : brightness), 0, 100);
  }

  private numberValue(dataArg: Record<string, unknown> | undefined, ...keysArg: string[]): number | undefined {
    if (!dataArg) {
      return undefined;
    }
    for (const key of keysArg) {
      const value = dataArg[key];
      if (typeof value === 'number' && Number.isFinite(value)) {
        return value;
      }
      if (typeof value === 'string' && value.trim() && Number.isFinite(Number(value))) {
        return Number(value);
      }
    }
    return undefined;
  }

  private stringValue(dataArg: Record<string, unknown> | undefined, ...keysArg: string[]): string | undefined {
    if (!dataArg) {
      return undefined;
    }
    for (const key of keysArg) {
      const value = dataArg[key];
      if (typeof value === 'string' && value) {
        return value;
      }
    }
    return undefined;
  }

  private clamp(valueArg: number, minArg: number, maxArg: number): number {
    return Math.max(minArg, Math.min(maxArg, valueArg));
  }
}
