import { constants as fsConstants } from "node:fs";
import { access } from "node:fs/promises";
import path from "node:path";

export const BUILDABLE_IMPLEMENTATION_STATES = Object.freeze([
  "repo-entrypoint",
  "static-web-wrapper",
  "repo-bundle"
]);

const buildableImplementationStates = new Set(BUILDABLE_IMPLEMENTATION_STATES);

async function pathExists(repoRoot, relativePath) {
  try {
    await access(path.join(repoRoot, relativePath), fsConstants.R_OK);
    return true;
  } catch {
    return false;
  }
}

export function publishEnabledForService(service) {
  return service.sourceState !== "intentionally-disabled" &&
    buildableImplementationStates.has(service.implementationState);
}

export function notPublishedReasonForService(service) {
  if (publishEnabledForService(service)) {
    return "publish_not_run";
  }
  if (service.sourceState === "intentionally-disabled") {
    return "disabled_intentionally";
  }
  if (service.implementationState === "missing-runtime-entrypoint") {
    return "disabled_missing_runtime_entrypoint";
  }
  if (service.implementationState === "library-only") {
    return "disabled_missing_executable_entrypoint";
  }
  return `disabled_${service.implementationState}`;
}

export function servicePublishPolicy(service) {
  const publishEnabled = publishEnabledForService(service);
  return {
    publishEnabled,
    artifactRequired: publishEnabled,
    artifactScope: publishEnabled ? "required" : "disabled",
    notPublishedReason: publishEnabled ? "publish_not_run" : notPublishedReasonForService(service)
  };
}

export function withServicePublishPolicy(service) {
  return {
    ...service,
    ...servicePublishPolicy(service)
  };
}

function serviceSourceState(catalog, serviceId) {
  return catalog?.services?.find((service) => service.serviceId === serviceId)?.sourceState ?? "source-present";
}

export async function resolveDevArtifactService(repoRoot, serviceId, catalog = null) {
  const sourceState = serviceSourceState(catalog, serviceId);
  const tsCmdPath = `cmd/${serviceId}/main.ts`;
  if (await pathExists(repoRoot, tsCmdPath)) {
    return withServicePublishPolicy({
      serviceId,
      runtimeKind: "bun-command",
      entrypoint: tsCmdPath,
      implementationState: "repo-entrypoint",
      sourceState
    });
  }

  const cmdPath = `cmd/${serviceId}/main.mjs`;
  if (await pathExists(repoRoot, cmdPath)) {
    return withServicePublishPolicy({
      serviceId,
      runtimeKind: "node-command",
      entrypoint: cmdPath,
      implementationState: "repo-entrypoint",
      sourceState
    });
  }

  const goCmdPath = `cmd/${serviceId}/main.go`;
  if (await pathExists(repoRoot, goCmdPath)) {
    return withServicePublishPolicy({
      serviceId,
      runtimeKind: "go-service",
      entrypoint: goCmdPath,
      implementationState: "repo-entrypoint",
      sourceState
    });
  }

  if (serviceId === "hwlab-cloud-web") {
    return withServicePublishPolicy({
      serviceId,
      runtimeKind: "cloud-web",
      entrypoint: "web/hwlab-cloud-web/index.html",
      implementationState: "static-web-wrapper",
      sourceState
    });
  }

  if (serviceId === "hwlab-agent-skills") {
    return withServicePublishPolicy({
      serviceId,
      runtimeKind: "skills-bundle",
      entrypoint: "skills/hwlab-agent-runtime/SKILL.md",
      implementationState: "repo-bundle",
      sourceState
    });
  }

  return withServicePublishPolicy({
    serviceId,
    runtimeKind: "health-placeholder",
    entrypoint: null,
    implementationState: "missing-runtime-entrypoint",
    sourceState
  });
}

export async function resolveDevArtifactServices(repoRoot, serviceIds, frozenServiceIds, catalog = null) {
  const unknown = serviceIds.filter((serviceId) => !frozenServiceIds.includes(serviceId));
  if (unknown.length > 0) {
    throw new Error(`unknown service IDs: ${unknown.join(", ")}`);
  }

  const services = [];
  for (const serviceId of serviceIds) {
    services.push(await resolveDevArtifactService(repoRoot, serviceId, catalog));
  }
  return services;
}

export function serviceInventoryFromServices(services) {
  const requiredServiceIds = services
    .filter((service) => service.artifactRequired)
    .map((service) => service.serviceId);
  const disabledServiceIds = services
    .filter((service) => !service.artifactRequired)
    .map((service) => service.serviceId);

  return {
    version: "v2",
    serviceCount: services.length,
    requiredServiceCount: requiredServiceIds.length,
    disabledServiceCount: disabledServiceIds.length,
    requiredServiceIds,
    disabledServiceIds,
    services: services.map((service) => ({
      serviceId: service.serviceId,
      publishEnabled: service.publishEnabled,
      artifactRequired: service.artifactRequired,
      artifactScope: service.artifactScope,
      runtimeKind: service.runtimeKind,
      implementationState: service.implementationState,
      sourceState: service.sourceState,
      entrypoint: service.entrypoint,
      disabledReason: service.artifactRequired ? null : service.notPublishedReason
    }))
  };
}
