import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TSimpleLocalRawData } from '../../core/index.js';
import type { IMcpConfig, IMcpRawState } from './mcp.types.js';
import { mcpProfile } from './mcp.types.js';

export class McpMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IMcpConfig>, 'profile'>): ISimpleLocalSnapshot {
    const rawState = mcpRawState(optionsArg.rawData) || mcpRawState(optionsArg.config.rawData);
    if (rawState) {
      return mcpSnapshot(optionsArg.config, rawState, optionsArg.online ?? rawState.initialized, optionsArg.source || 'http', optionsArg.error);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: mcpProfile });
  }

  public static toSnapshotFromRaw(configArg: IMcpConfig, rawDataArg: TSimpleLocalRawData): ISimpleLocalSnapshot {
    return this.toSnapshot({ config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

  public static toDevices(snapshotArg: ISimpleLocalSnapshot): shxInterfaces.data.IDeviceDefinition[] {
    return SimpleLocalMapper.toDevices(mcpProfile, snapshotArg);
  }

  public static toEntities(snapshotArg: ISimpleLocalSnapshot): IIntegrationEntity[] {
    return SimpleLocalMapper.toEntities(mcpProfile, snapshotArg);
  }

  public static slug(valueArg: unknown): string {
    return SimpleLocalMapper.slug(valueArg);
  }

  public static clone<TValue>(valueArg: TValue): TValue {
    return SimpleLocalMapper.clone(valueArg);
  }
}

const mcpSnapshot = (configArg: IMcpConfig, rawDataArg: IMcpRawState, onlineArg: boolean, sourceArg: ISimpleLocalSnapshot['source'], errorArg?: string): ISimpleLocalSnapshot => {
  const endpoint = rawDataArg.endpointUrl || configArg.url || endpointFromHost(configArg);
  const serverName = configArg.name || rawDataArg.serverInfo?.name || mcpProfile.defaultName || mcpProfile.displayName;
  return {
    device: {
      id: configArg.uniqueId || endpoint || rawDataArg.serverInfo?.name || mcpProfile.domain,
      name: serverName,
      manufacturer: mcpProfile.manufacturer,
      model: 'MCP streamable HTTP server',
      protocol: endpoint?.startsWith('https://') ? 'https' : 'http',
      configurationUrl: endpoint,
      attributes: cleanAttributes({ protocolVersion: rawDataArg.protocolVersion, serverVersion: rawDataArg.serverInfo?.version, ...configArg.metadata }),
    },
    entities: mcpEntities(rawDataArg, onlineArg),
    online: onlineArg,
    updatedAt: rawDataArg.fetchedAt || new Date().toISOString(),
    source: sourceArg,
    rawData: rawDataArg,
    error: errorArg,
    capabilities: {
      localRead: true,
      localControl: true,
      streamableHttp: true,
    },
  };
};

const mcpEntities = (rawDataArg: IMcpRawState, onlineArg: boolean): ISimpleLocalEntitySnapshot[] => {
  const entities: ISimpleLocalEntitySnapshot[] = [
    {
      id: 'initialized',
      name: 'Initialized',
      platform: 'binary_sensor',
      state: rawDataArg.initialized,
      available: onlineArg,
      writable: false,
    },
    {
      id: 'tool_count',
      name: 'Tool Count',
      platform: 'sensor',
      state: rawDataArg.toolCount,
      available: onlineArg,
      writable: false,
      attributes: cleanAttributes({ protocolVersion: rawDataArg.protocolVersion, serverName: rawDataArg.serverInfo?.name }),
    },
  ];

  for (const tool of rawDataArg.tools) {
    entities.push({
      id: `tool_${SimpleLocalMapper.slug(tool.name)}`,
      uniqueId: `mcp_tool_${SimpleLocalMapper.slug(tool.name)}`,
      name: `Tool ${tool.name}`,
      platform: 'sensor',
      state: 'available',
      available: onlineArg,
      writable: true,
      attributes: cleanAttributes({ description: tool.description, inputSchema: tool.inputSchema }),
    });
  }

  return entities;
};

const mcpRawState = (valueArg: unknown): IMcpRawState | undefined => {
  if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
    return undefined;
  }
  const value = valueArg as Partial<IMcpRawState>;
  if (Array.isArray(value.tools) && typeof value.toolCount === 'number' && typeof value.initialized === 'boolean') {
    return value as IMcpRawState;
  }
  return undefined;
};

const endpointFromHost = (configArg: IMcpConfig): string | undefined => {
  if (!configArg.host) {
    return undefined;
  }
  const protocol = configArg.useTls ? 'https' : 'http';
  const port = configArg.port ? `:${configArg.port}` : '';
  const path = configArg.path || mcpProfile.defaultHttpPath || '/mcp';
  return `${protocol}://${configArg.host}${port}${path.startsWith('/') ? path : `/${path}`}`;
};

const cleanAttributes = (attributesArg: Record<string, unknown>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};
