import { getRunningMode } from '../initialData/getSupportedRunModes.js';
import { CloudMessageModel } from '../model/CloudMessageModel.js';
import { hasDockingStationError } from '../model/DockingStationStatus.js';
import { RoborockMatterbridgePlatform } from '../platform.js';
import { AdditionalPropCode, DeviceStatusNotify, Protocol } from '../roborockCommunication/index.js';
import { state_to_matter_operational_status, state_to_matter_state } from '../share/function.js';
import { RvcCleanMode, RvcOperationalState, RvcRunMode, ServiceArea } from 'matterbridge/matter/clusters';
import { triggerDssError } from './handleLocalMessage.js';
import { DpsPayload } from '../roborockCommunication/broadcast/model/dps.js';
import { getRoomMapFromDevice, isStatusUpdate } from '../helper.js';
import { debugStringify } from 'matterbridge/logger';
import { CloudMessageResult } from '../roborockCommunication/Zmodel/messageResult.js';
import { NotifyMessageTypes } from '../notifyMessageTypes.js';
import { getCurrentCleanModeFunc } from '../share/runtimeHelper.js';
import { getSupportedAreas } from '../initialData/getSupportedAreas.js';
import { PlatformRunner } from '../platformRunner.js';
import { RoborockVacuumCleaner } from '../rvc.js';

export async function handleCloudMessage(data: CloudMessageModel, platform: RoborockMatterbridgePlatform, runner: PlatformRunner, duid: string): Promise<void> {
  const messageTypes = Object.keys(data.dps).map(Number);
  const robot = platform.robots.get(duid);
  if (robot === undefined) {
    platform.log.error(`Error3: Robot with DUID ${duid} not found`);
    return;
  }

  // Known: 122, 121, 102,
  // Unknown: 128, 139
  messageTypes.forEach(async (messageType) => {
    switch (messageType) {
      case Protocol.status_update: {
        const status = Number(data.dps[messageType]);
        const matterState = state_to_matter_state(status);

        if (matterState) {
          robot.updateAttribute(RvcRunMode.Cluster.id, 'currentMode', getRunningMode(matterState), platform.log);
        }

        const operationalStateId = state_to_matter_operational_status(status);
        if (operationalStateId) {
          const dssHasError = hasDockingStationError(robot.dockStationStatus);
          if (!(dssHasError && triggerDssError(robot, platform))) {
            robot.updateAttribute(RvcOperationalState.Cluster.id, 'operationalState', operationalStateId, platform.log);
          }
        }
        break;
      }
      case Protocol.rpc_response: {
        const response = data.dps[messageType] as DpsPayload;
        // ignore network info
        if (!isStatusUpdate(response.result)) {
          platform.log.debug('Ignore message:', debugStringify(data));
          return;
        }

        let roboStatus: CloudMessageResult | undefined;
        if (Array.isArray(response.result) && response.result.length > 0) {
          roboStatus = response.result[0] as CloudMessageResult;
        }

        if (roboStatus) {
          const message = { ...roboStatus } as DeviceStatusNotify;
          platform.log.debug('rpc_response:', debugStringify(message));
          await runner.updateFromMQTTMessage(NotifyMessageTypes.LocalMessage, message, duid, true);
        }
        break;
      }
      case Protocol.suction_power:
      case Protocol.water_box_mode: {
        await platform.roborockService?.getCleanModeData(duid).then((cleanModeData) => {
          if (cleanModeData) {
            const currentCleanMode = getCurrentCleanModeFunc(
              robot.device.data.model,
              platform.enableExperimentalFeature?.advancedFeature?.forceRunAtDefault ?? false,
            )({
              suctionPower: cleanModeData.suctionPower,
              waterFlow: cleanModeData.waterFlow,
              distance_off: cleanModeData.distance_off,
              mopRoute: cleanModeData.mopRoute,
            });

            platform.log.debug(`Clean mode data: ${debugStringify(cleanModeData)}`);
            platform.log.debug(`Current clean mode: ${currentCleanMode}`);
            if (currentCleanMode) {
              robot.updateAttribute(RvcCleanMode.Cluster.id, 'currentMode', currentCleanMode, platform.log);
            }
          }
        });
        break; // Do nothing, handled in local message
      }
      case Protocol.additional_props: {
        platform.log.notice(`Received additional properties for robot ${duid}: ${debugStringify(data)}`);
        const propCode = data.dps[Protocol.additional_props] as number;
        platform.log.debug(`DPS for additional properties: ${propCode}, AdditionalPropCode: ${AdditionalPropCode[propCode]}`);
        if (propCode === AdditionalPropCode.map_change) {
          await handleMapChange(robot, platform, duid);
        }
        break;
      }
      case Protocol.back_type: {
        // TODO: check if this is needed
        break;
      }
      default: {
        platform.log.notice(`Unknown message type ${messageType}, protocol: ${Protocol[messageType]}, message: ${debugStringify(data)}`);
        break;
      }
    }
  });
}

export async function handleMapChange(robot: RoborockVacuumCleaner, platform: RoborockMatterbridgePlatform, duid: string): Promise<void> {
  const enableMultipleMap = (platform.enableExperimentalFeature?.enableExperimentalFeature && platform.enableExperimentalFeature?.advancedFeature?.enableMultipleMap) ?? false;
  if (!enableMultipleMap) return;

  await getRoomMapFromDevice(robot.device, platform).then((roomMap) => {
    const { supportedAreas, supportedMaps, roomIndexMap } = getSupportedAreas(robot.device.rooms, roomMap, enableMultipleMap, platform.log);

    platform.log.debug(`handleMapChange - supportedAreas: ${debugStringify(supportedAreas)}`);
    platform.log.debug(`handleMapChange - supportedMaps: ${debugStringify(supportedMaps)}`);
    platform.log.debug(`handleMapChange - roomIndexMap: `, roomIndexMap);

    platform.roborockService?.setSupportedAreas(duid, supportedAreas);
    platform.roborockService?.setSelectedAreas(duid, []);
    robot.updateAttribute(ServiceArea.Cluster.id, 'supportedAreas', supportedAreas, platform.log);
    robot.updateAttribute(ServiceArea.Cluster.id, 'selectedAreas', [], platform.log);
    robot.updateAttribute(ServiceArea.Cluster.id, 'currentArea', null, platform.log);

    if (enableMultipleMap) {
      platform.roborockService?.setSupportedAreaIndexMap(duid, roomIndexMap);
      robot.updateAttribute(ServiceArea.Cluster.id, 'supportedMaps', supportedMaps, platform.log);
    }
  });
}
