import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { BaseIntegration } from '../../core/classes.baseintegration.js';
import type { IIntegrationEntity, IIntegrationEvent, IIntegrationRuntime, IIntegrationSetupContext, IServiceCallRequest, IServiceCallResult } from '../../core/types.js';
import { OpenthermGwClient } from './opentherm_gw.classes.client.js';
import { OpenthermGwConfigFlow } from './opentherm_gw.classes.configflow.js';
import { createOpenthermGwDiscoveryDescriptor } from './opentherm_gw.discovery.js';
import { OpenthermGwMapper } from './opentherm_gw.mapper.js';
import type { IOpenthermGwConfig, TOpenthermGwCommandValue } from './opentherm_gw.types.js';
import { openthermGwDomain } from './opentherm_gw.types.js';

export class OpenthermGwIntegration extends BaseIntegration<IOpenthermGwConfig> {
  public readonly domain = openthermGwDomain;
  public readonly displayName = 'OpenTherm Gateway';
  public readonly status = 'control-runtime' as const;
  public readonly discoveryDescriptor = createOpenthermGwDiscoveryDescriptor();
  public readonly configFlow = new OpenthermGwConfigFlow();
  public readonly metadata = {
    source: 'home-assistant/core',
    upstreamPath: 'homeassistant/components/opentherm_gw',
    upstreamDomain: openthermGwDomain,
    integrationType: 'device',
    iotClass: 'local_push',
    requirements: ['pyotgw==2.2.3'],
    dependencies: [],
    afterDependencies: [],
    codeowners: ['@mvn23'],
    configFlow: true,
    documentation: 'https://www.home-assistant.io/integrations/opentherm_gw',
  };

  public async setup(configArg: IOpenthermGwConfig, contextArg: IIntegrationSetupContext): Promise<IIntegrationRuntime> {
    void contextArg;
    return new OpenthermGwRuntime(new OpenthermGwClient(configArg));
  }

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

export class HomeAssistantOpenthermGwIntegration extends OpenthermGwIntegration {}

class OpenthermGwRuntime implements IIntegrationRuntime {
  public domain = openthermGwDomain;

  constructor(private readonly client: OpenthermGwClient) {}

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

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

  public async subscribe(handlerArg: (eventArg: IIntegrationEvent) => void): Promise<() => Promise<void>> {
    const unsubscribe = this.client.subscribe((eventArg) => handlerArg(OpenthermGwMapper.toIntegrationEvent(eventArg)));
    return async () => unsubscribe();
  }

  public async callService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    try {
      if (requestArg.domain === openthermGwDomain) {
        return await this.callGatewayService(requestArg);
      }
      if (requestArg.domain === 'climate') {
        return await this.callClimateService(requestArg);
      }
      if (requestArg.domain === 'switch') {
        return await this.callSwitchService(requestArg);
      }
      return { success: false, error: `Unsupported OpenTherm Gateway 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 callGatewayService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service === 'set_control_setpoint') {
      return { success: true, data: await this.client.setControlSetpoint(this.numberValue(requestArg.data?.temperature ?? requestArg.data?.setpoint, 'set_control_setpoint requires data.temperature.')) };
    }
    if (requestArg.service === 'set_hot_water') {
      if (requestArg.data?.temperature !== undefined) {
        return { success: true, data: await this.client.setHotWaterSetpoint(this.numberValue(requestArg.data.temperature, 'set_hot_water temperature must be a number.')) };
      }
      return { success: true, data: await this.client.setHotWater(this.hotWaterValue(requestArg.data)) };
    }
    if (requestArg.service === 'set_hot_water_ovrd') {
      return { success: true, data: await this.client.setHotWater(this.hotWaterValue(requestArg.data)) };
    }
    if (requestArg.service === 'set_hot_water_setpoint') {
      return { success: true, data: await this.client.setHotWaterSetpoint(this.numberValue(requestArg.data?.temperature, 'set_hot_water_setpoint requires data.temperature.')) };
    }
    if (requestArg.service === 'set_outside_temperature') {
      return { success: true, data: await this.client.setOutsideTemperature(this.numberValue(requestArg.data?.temperature, 'set_outside_temperature requires data.temperature.')) };
    }
    if (requestArg.service === 'set_central_heating_ovrd') {
      return { success: true, data: await this.client.setCentralHeatingOverride(1, this.booleanValue(requestArg.data?.ch_override ?? requestArg.data?.enabled, 'set_central_heating_ovrd requires data.ch_override or data.enabled.')) };
    }
    if (requestArg.service === 'reset' || requestArg.service === 'reset_gateway') {
      return { success: true, data: await this.client.reset() };
    }
    if (requestArg.service === 'command' || requestArg.service === 'send_transparent_command') {
      const code = this.stringValue(requestArg.data?.command ?? requestArg.data?.transp_cmd, 'OpenTherm Gateway command service requires data.command.');
      const value = this.commandValue(requestArg.data?.value ?? requestArg.data?.transp_arg);
      return { success: true, data: await this.client.command(code, value) };
    }
    return { success: false, error: `Unsupported OpenTherm Gateway service: ${requestArg.service}` };
  }

  private async callClimateService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'set_temperature') {
      return { success: false, error: `Unsupported OpenTherm Gateway climate service: ${requestArg.service}` };
    }
    const temperature = this.numberValue(requestArg.data?.temperature, 'climate.set_temperature requires data.temperature.');
    const temporary = typeof requestArg.data?.temporary === 'boolean' ? requestArg.data.temporary : undefined;
    return { success: true, data: await this.client.setRoomSetpoint(temperature, temporary) };
  }

  private async callSwitchService(requestArg: IServiceCallRequest): Promise<IServiceCallResult> {
    if (requestArg.service !== 'turn_on' && requestArg.service !== 'turn_off') {
      return { success: false, error: `Unsupported OpenTherm Gateway switch service: ${requestArg.service}` };
    }
    const target = `${requestArg.target.entityId || requestArg.target.deviceId || ''} ${requestArg.data?.key || ''}`;
    const circuit = target.includes('2') ? 2 : 1;
    return { success: true, data: await this.client.setCentralHeatingOverride(circuit, requestArg.service === 'turn_on') };
  }

  private hotWaterValue(dataArg: Record<string, unknown> | undefined): boolean | string | number {
    const value = dataArg?.enabled ?? dataArg?.hot_water ?? dataArg?.hotWater ?? dataArg?.dhw_override ?? dataArg?.dhwOverride ?? dataArg?.value;
    if (typeof value === 'boolean' || typeof value === 'string' || typeof value === 'number') {
      return value;
    }
    throw new Error('set_hot_water requires data.enabled, data.hot_water, or data.dhw_override unless data.temperature is used.');
  }

  private commandValue(valueArg: unknown): TOpenthermGwCommandValue {
    if (typeof valueArg === 'string' || typeof valueArg === 'number' || typeof valueArg === 'boolean') {
      return valueArg;
    }
    throw new Error('OpenTherm Gateway command service requires data.value.');
  }

  private numberValue(valueArg: unknown, errorArg: string): number {
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
      return valueArg;
    }
    if (typeof valueArg === 'string' && valueArg.trim()) {
      const parsed = Number(valueArg);
      if (Number.isFinite(parsed)) {
        return parsed;
      }
    }
    throw new Error(errorArg);
  }

  private booleanValue(valueArg: unknown, errorArg: string): boolean {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (valueArg === '1' || valueArg === 1 || valueArg === 'true') {
      return true;
    }
    if (valueArg === '0' || valueArg === 0 || valueArg === 'false') {
      return false;
    }
    throw new Error(errorArg);
  }

  private stringValue(valueArg: unknown, errorArg: string): string {
    if (typeof valueArg === 'string' && valueArg.trim()) {
      return valueArg.trim().toUpperCase();
    }
    throw new Error(errorArg);
  }
}
