import { ENVIRONMENT_DEV } from "../protocol/index.mjs";
import { buildMetadataFromEnv } from "../build-metadata.mjs";

export const DEFAULT_PROJECT_ID = "prj_mvp_topology";
export const DEFAULT_GATEWAY_SESSION_ID = "gws_mvp_simu";

export const SIM_PORT_DEFINITIONS = Object.freeze([
  {
    port: "AI1",
    family: "AI",
    direction: "input",
    valueType: "number",
    unit: "V",
    initialValue: 0
  },
  {
    port: "AO1",
    family: "AO",
    direction: "output",
    valueType: "number",
    unit: "V",
    initialValue: 0
  },
  {
    port: "DI1",
    family: "DI",
    direction: "input",
    valueType: "boolean",
    initialValue: false
  },
  {
    port: "DO1",
    family: "DO",
    direction: "output",
    valueType: "boolean",
    initialValue: false
  },
  {
    port: "FREQ1",
    family: "FREQ",
    direction: "bidirectional",
    valueType: "number",
    unit: "Hz",
    initialValue: 0
  },
  {
    port: "FREQ_IN1",
    family: "FREQ_IN",
    direction: "input",
    valueType: "number",
    unit: "Hz",
    initialValue: 0
  },
  {
    port: "FREQ_OUT1",
    family: "FREQ_OUT",
    direction: "output",
    valueType: "number",
    unit: "Hz",
    initialValue: 0
  },
  {
    port: "uart0",
    family: "SERIAL",
    direction: "bidirectional",
    valueType: "string",
    initialValue: null,
    legacy: true
  },
  {
    port: "eth0",
    family: "NETWORK",
    direction: "bidirectional",
    valueType: "object",
    initialValue: null,
    legacy: true
  },
  {
    port: "gpio0",
    family: "GPIO",
    direction: "bidirectional",
    valueType: "boolean",
    initialValue: null,
    legacy: true
  }
]);

export const SIM_PORTS = Object.freeze(SIM_PORT_DEFINITIONS.map((definition) => definition.port));

export const L2_PORT_FAMILIES = Object.freeze(["AI", "AO", "DI", "DO", "FREQ", "FREQ_IN", "FREQ_OUT"]);

function isoNow() {
  return new Date().toISOString();
}

export function selectInstanceValue(
  value,
  fallback,
  {
    index = process.env.HWLAB_INSTANCE_INDEX,
    hostname = process.env.HOSTNAME,
    requireIndexForMultiple = false,
    label = "instance value"
  } = {}
) {
  const items = String(value ?? "")
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean);

  if (items.length === 0) {
    return fallback;
  }
  if (items.length === 1) {
    return items[0];
  }

  const parsedIndex = Number.parseInt(index ?? "", 10);
  if (Number.isInteger(parsedIndex) && parsedIndex >= 0 && parsedIndex < items.length) {
    return items[parsedIndex];
  }

  const ordinal = Number.parseInt(String(hostname ?? "").match(/-(\d+)$/)?.[1] ?? "", 10);
  if (Number.isInteger(ordinal) && ordinal >= 0 && ordinal < items.length) {
    return items[ordinal];
  }

  if (requireIndexForMultiple) {
    throw new Error(
      `${label} declares ${items.length} indexed values, but neither HWLAB_INSTANCE_INDEX nor a StatefulSet ordinal HOSTNAME selected one`
    );
  }

  return items[0];
}

export function stableIdPart(value) {
  const normalized = String(value ?? "")
    .trim()
    .replace(/[^A-Za-z0-9._:-]+/g, "_")
    .replace(/^_+|_+$/g, "");

  return normalized || "sim";
}

export function resourceIdForBox(boxId) {
  return `res_${stableIdPart(boxId)}`;
}

export function createBoxResource({
  boxId,
  gatewaySessionId = DEFAULT_GATEWAY_SESSION_ID,
  ports = SIM_PORTS,
  portFamilies = L2_PORT_FAMILIES,
  now = isoNow()
}) {
  return {
    resourceId: resourceIdForBox(boxId),
    projectId: DEFAULT_PROJECT_ID,
    gatewaySessionId,
    boxId,
    resourceType: "simulator_endpoint",
    name: `${boxId} simulator endpoint`,
    state: "available",
    environment: ENVIRONMENT_DEV,
    metadata: {
      serviceId: "hwlab-box-simu",
      ports,
      ...(portFamilies.length > 0 ? { portFamilies } : {}),
      propagation: "patch-panel-only"
    },
    createdAt: now,
    updatedAt: now
  };
}

export function createBoxCapabilities({
  boxId,
  resourceId = resourceIdForBox(boxId),
  projectId = DEFAULT_PROJECT_ID,
  portDefinitions = SIM_PORT_DEFINITIONS,
  now = isoNow()
}) {
  const capabilities = [];
  const boxPart = stableIdPart(boxId);

  for (const definition of portDefinitions) {
    if (definition.legacy) {
      continue;
    }

    const portPart = definition.port.toLowerCase();
    const family = definition.family.toLowerCase();
    capabilities.push({
      capabilityId: `cap_${boxPart}_${portPart}_read`,
      resourceId,
      projectId,
      name: `${family}.read`,
      description: `Read ${definition.port} on ${boxId}.`,
      direction: "input",
      valueType: definition.valueType,
      ...(definition.unit ? { unit: definition.unit } : {}),
      constraints: {
        port: definition.port,
        portFamily: definition.family,
        internalPortApi: true,
        patchPanelOnlyPropagation: true
      },
      mutatesState: false,
      createdAt: now,
      updatedAt: now
    });

    if (definition.direction === "output" || definition.direction === "bidirectional") {
      capabilities.push({
        capabilityId: `cap_${boxPart}_${portPart}_write`,
        resourceId,
        projectId,
        name: `${family}.write`,
        description: `Write ${definition.port} on ${boxId}.`,
        direction: definition.direction === "bidirectional" ? "bidirectional" : "output",
        valueType: definition.valueType,
        ...(definition.unit ? { unit: definition.unit } : {}),
        constraints: {
          port: definition.port,
          portFamily: definition.family,
          internalPortApi: false,
          patchPanelOnlyPropagation: true
        },
        mutatesState: true,
        createdAt: now,
        updatedAt: now
      });
    }
  }

  return capabilities;
}

export function getPortDefinition(port) {
  return SIM_PORT_DEFINITIONS.find((definition) => definition.port === port);
}

export function createGatewaySession({
  gatewayId,
  serviceId = "hwlab-gateway-simu",
  endpoint,
  now = isoNow()
}) {
  return {
    gatewaySessionId: `gws_${gatewayId}`,
    projectId: DEFAULT_PROJECT_ID,
    serviceId,
    gatewayId,
    endpoint,
    status: "connected",
    environment: ENVIRONMENT_DEV,
    startedAt: now,
    lastSeenAt: now,
    labels: {
      simulator: serviceId === "hwlab-gateway-simu" ? "true" : "false"
    }
  };
}

export function createBoxState({
  boxId,
  gatewaySessionId = DEFAULT_GATEWAY_SESSION_ID,
  ports: portNames = SIM_PORTS,
  includePortDefinitions = true,
  now = isoNow()
}) {
  const selectedDefinitions = portNames
    .map((port) => getPortDefinition(port) ?? {
      port,
      family: port,
      direction: "bidirectional",
      valueType: "object",
      initialValue: null,
      legacy: !includePortDefinitions
    });
  const ports = Object.fromEntries(
    selectedDefinitions.map((definition) => [
      definition.port,
      {
        port: definition.port,
        ...(includePortDefinitions
          ? {
              family: definition.family,
              direction: definition.direction,
              valueType: definition.valueType,
              ...(definition.unit ? { unit: definition.unit } : {}),
              internalPortApi: true
            }
          : {}),
        value: definition.initialValue,
        source: "local",
        updatedAt: now
      }
    ])
  );
  const portFamilies = includePortDefinitions
    ? [...new Set(selectedDefinitions.map((definition) => definition.family))]
    : [];
  const resource = createBoxResource({
    boxId,
    gatewaySessionId,
    ports: portNames,
    portFamilies,
    now
  });

  return {
    serviceId: "hwlab-box-simu",
    boxId,
    projectId: DEFAULT_PROJECT_ID,
    ...(includePortDefinitions ? { environment: ENVIRONMENT_DEV } : {}),
    gatewaySessionId,
    live: true,
    resource,
    ...(includePortDefinitions
      ? {
          capabilities: createBoxCapabilities({
            boxId,
            resourceId: resource.resourceId,
            projectId: DEFAULT_PROJECT_ID,
            portDefinitions: selectedDefinitions,
            now
          })
        }
      : {}),
    ports,
    constraints: {
      crossDevicePropagation: "patch-panel-only",
      localLoopbackEnabled: false
    },
    updatedAt: now
  };
}

export function createGatewayState({
  gatewayId,
  serviceId = "hwlab-gateway-simu",
  endpoint,
  boxes = [],
  now = isoNow()
}) {
  return {
    serviceId,
    gatewayId,
    projectId: DEFAULT_PROJECT_ID,
    live: true,
    session: createGatewaySession({ gatewayId, serviceId, endpoint, now }),
    boxes,
    updatedAt: now
  };
}

export function createHealth({ serviceId, name, now = isoNow() }) {
  const metadata = buildMetadataFromEnv(process.env, { serviceId });
  return {
    serviceId,
    name,
    environment: ENVIRONMENT_DEV,
    live: true,
    status: "ok",
    revision: metadata.revision,
    commit: metadata.commit,
    image: metadata.image,
    build: metadata.build,
    observedAt: now
  };
}
