import z from "zod"
import * as Log from "@saeeol/core/util/log"
import { NamedError } from "@saeeol/core/util/error"
import { Bus } from "@/bus"
import { ConfigError } from "../../config/error"
import type { Config } from "../../config/config"

const log = Log.create({ service: "saeeol.config" })

export function toWarning(err: unknown): Config.Warning | undefined {
  if (ConfigError.JsonError.isInstance(err))
    return {
      path: err.data.path,
      message: `Config file at ${err.data.path} is not valid JSON(C)`,
      detail: err.data.message || undefined,
    }
  if (ConfigError.InvalidError.isInstance(err)) {
    const text = err.data.issues ? formatIssues(err.data.issues) : err.data.message
    return {
      path: err.data.path,
      message: text
        ? `Configuration is invalid at ${err.data.path}: ${text}`
        : `Configuration is invalid at ${err.data.path}`,
    }
  }
  return undefined
}

export function formatIssues(issues: z.core.$ZodIssue[]) {
  return issues
    .map((issue) => {
      const loc = issue.path.map(String).join(".")
      if (!loc) return issue.message
      return `${loc}: ${issue.message}`
    })
    .join("\n")
}

export async function handleInvalid(
  kind: "agent" | "command",
  item: string,
  issues: z.core.$ZodIssue[],
  cause: Error,
  warnings?: Config.Warning[],
) {
  const text = formatIssues(issues)
  const message = text ? `Config file at ${item} is invalid: ${text}` : `Config file at ${item} is invalid`
  const err = new ConfigError.InvalidError({ path: item, issues }, { cause })
  if (warnings) warnings.push({ path: item, message, detail: text || undefined })
  try {
    const { Session } = await import("@/session/session")
    Bus.publish(Session.Event.Error, { error: new NamedError.Unknown({ message }).toObject() })
  } catch (e) {
    log.warn("could not publish session error", { message, err: e })
  }
  if (kind === "command") {
    log.error("failed to load command", { command: item, err, message })
    return
  }
  log.error("failed to load agent", { agent: item, err, message })
}

export function caught(warnings: Config.Warning[], source: string, err: unknown) {
  const w = toWarning(err)
  if (w) {
    warnings.push(w)
    log.warn("skipped config due to error", { source, err })
    return
  }
  throw err
}
