import os from "node:os";
import path from "node:path";

import type { ClawdbotConfig } from "../config/config.js";
import {
  CONFIG_PATH_CLAWDBOT,
  createConfigIO,
  migrateLegacyConfig,
  readConfigFileSnapshot,
  writeConfigFile,
} from "../config/config.js";
import type { RuntimeEnv } from "../runtime.js";
import { note } from "../terminal/note.js";
import { resolveUserPath } from "../utils.js";

function resolveLegacyConfigPath(env: NodeJS.ProcessEnv): string {
  const override = env.CLAWDIS_CONFIG_PATH?.trim();
  if (override) return override;
  return path.join(os.homedir(), ".clawdis", "clawdis.json");
}

function normalizeDefaultWorkspacePath(
  value: string | undefined,
): string | undefined {
  if (!value) return value;

  const resolved = resolveUserPath(value);
  const home = os.homedir();

  const next = [
    ["clawdis", "clawd"],
    ["clawdbot", "clawd"],
  ].reduce((acc, [from, to]) => {
    const fromPrefix = path.join(home, from);
    if (acc === fromPrefix) return path.join(home, to);
    const withSep = `${fromPrefix}${path.sep}`;
    if (acc.startsWith(withSep)) {
      return path.join(home, to).concat(acc.slice(fromPrefix.length));
    }
    return acc;
  }, resolved);

  return next === resolved ? value : next;
}

export function replaceLegacyName(
  value: string | undefined,
): string | undefined {
  if (!value) return value;
  const replacedClawdis = value.replace(/clawdis/g, "clawdbot");
  return replacedClawdis.replace(/clawd(?!bot)/g, "clawdbot");
}

export function replaceModernName(
  value: string | undefined,
): string | undefined {
  if (!value) return value;
  if (!value.includes("clawdbot")) return value;
  return value.replace(/clawdbot/g, "clawdis");
}

export function normalizeLegacyConfigValues(cfg: ClawdbotConfig): {
  config: ClawdbotConfig;
  changes: string[];
} {
  const changes: string[] = [];
  let next: ClawdbotConfig = cfg;

  const defaults = cfg.agents?.defaults;
  if (defaults) {
    let updatedDefaults = defaults;
    let defaultsChanged = false;

    const updatedWorkspace = normalizeDefaultWorkspacePath(defaults.workspace);
    if (updatedWorkspace && updatedWorkspace !== defaults.workspace) {
      updatedDefaults = { ...updatedDefaults, workspace: updatedWorkspace };
      defaultsChanged = true;
      changes.push(`Updated agents.defaults.workspace → ${updatedWorkspace}`);
    }

    const sandbox = defaults.sandbox;
    if (sandbox) {
      let updatedSandbox = sandbox;
      let sandboxChanged = false;

      const updatedWorkspaceRoot = normalizeDefaultWorkspacePath(
        sandbox.workspaceRoot,
      );
      if (
        updatedWorkspaceRoot &&
        updatedWorkspaceRoot !== sandbox.workspaceRoot
      ) {
        updatedSandbox = {
          ...updatedSandbox,
          workspaceRoot: updatedWorkspaceRoot,
        };
        sandboxChanged = true;
        changes.push(
          `Updated agents.defaults.sandbox.workspaceRoot → ${updatedWorkspaceRoot}`,
        );
      }

      const dockerImage = sandbox.docker?.image;
      const updatedDockerImage = replaceLegacyName(dockerImage);
      if (updatedDockerImage && updatedDockerImage !== dockerImage) {
        updatedSandbox = {
          ...updatedSandbox,
          docker: {
            ...updatedSandbox.docker,
            image: updatedDockerImage,
          },
        };
        sandboxChanged = true;
        changes.push(
          `Updated agents.defaults.sandbox.docker.image → ${updatedDockerImage}`,
        );
      }

      const containerPrefix = sandbox.docker?.containerPrefix;
      const updatedContainerPrefix = replaceLegacyName(containerPrefix);
      if (
        updatedContainerPrefix &&
        updatedContainerPrefix !== containerPrefix
      ) {
        updatedSandbox = {
          ...updatedSandbox,
          docker: {
            ...updatedSandbox.docker,
            containerPrefix: updatedContainerPrefix,
          },
        };
        sandboxChanged = true;
        changes.push(
          `Updated agents.defaults.sandbox.docker.containerPrefix → ${updatedContainerPrefix}`,
        );
      }

      if (sandboxChanged) {
        updatedDefaults = { ...updatedDefaults, sandbox: updatedSandbox };
        defaultsChanged = true;
      }
    }

    if (defaultsChanged) {
      next = {
        ...next,
        agents: {
          ...next.agents,
          defaults: updatedDefaults,
        },
      };
    }
  }

  const list = Array.isArray(cfg.agents?.list) ? cfg.agents.list : [];
  if (list.length > 0) {
    let listChanged = false;
    const nextList = list.map((agent) => {
      let updatedAgent = agent;
      let agentChanged = false;

      const updatedWorkspace = normalizeDefaultWorkspacePath(agent.workspace);
      if (updatedWorkspace && updatedWorkspace !== agent.workspace) {
        updatedAgent = { ...updatedAgent, workspace: updatedWorkspace };
        agentChanged = true;
        changes.push(
          `Updated agents.list (id "${agent.id}") workspace → ${updatedWorkspace}`,
        );
      }

      const sandbox = agent.sandbox;
      if (sandbox) {
        let updatedSandbox = sandbox;
        let sandboxChanged = false;

        const updatedWorkspaceRoot = normalizeDefaultWorkspacePath(
          sandbox.workspaceRoot,
        );
        if (
          updatedWorkspaceRoot &&
          updatedWorkspaceRoot !== sandbox.workspaceRoot
        ) {
          updatedSandbox = {
            ...updatedSandbox,
            workspaceRoot: updatedWorkspaceRoot,
          };
          sandboxChanged = true;
          changes.push(
            `Updated agents.list (id "${agent.id}") sandbox.workspaceRoot → ${updatedWorkspaceRoot}`,
          );
        }

        const dockerImage = sandbox.docker?.image;
        const updatedDockerImage = replaceLegacyName(dockerImage);
        if (updatedDockerImage && updatedDockerImage !== dockerImage) {
          updatedSandbox = {
            ...updatedSandbox,
            docker: {
              ...updatedSandbox.docker,
              image: updatedDockerImage,
            },
          };
          sandboxChanged = true;
          changes.push(
            `Updated agents.list (id "${agent.id}") sandbox.docker.image → ${updatedDockerImage}`,
          );
        }

        const containerPrefix = sandbox.docker?.containerPrefix;
        const updatedContainerPrefix = replaceLegacyName(containerPrefix);
        if (
          updatedContainerPrefix &&
          updatedContainerPrefix !== containerPrefix
        ) {
          updatedSandbox = {
            ...updatedSandbox,
            docker: {
              ...updatedSandbox.docker,
              containerPrefix: updatedContainerPrefix,
            },
          };
          sandboxChanged = true;
          changes.push(
            `Updated agents.list (id "${agent.id}") sandbox.docker.containerPrefix → ${updatedContainerPrefix}`,
          );
        }

        if (sandboxChanged) {
          updatedAgent = { ...updatedAgent, sandbox: updatedSandbox };
          agentChanged = true;
        }
      }

      if (agentChanged) listChanged = true;
      return agentChanged ? updatedAgent : agent;
    });

    if (listChanged) {
      next = {
        ...next,
        agents: {
          ...next.agents,
          list: nextList,
        },
      };
    }
  }

  const legacyAckReaction = cfg.messages?.ackReaction?.trim();
  if (legacyAckReaction) {
    const hasWhatsAppAck = cfg.whatsapp?.ackReaction !== undefined;
    if (!hasWhatsAppAck) {
      const legacyScope = cfg.messages?.ackReactionScope ?? "group-mentions";
      let direct = true;
      let group: "always" | "mentions" | "never" = "mentions";
      if (legacyScope === "all") {
        direct = true;
        group = "always";
      } else if (legacyScope === "direct") {
        direct = true;
        group = "never";
      } else if (legacyScope === "group-all") {
        direct = false;
        group = "always";
      } else if (legacyScope === "group-mentions") {
        direct = false;
        group = "mentions";
      }
      next = {
        ...next,
        whatsapp: {
          ...next.whatsapp,
          ackReaction: { emoji: legacyAckReaction, direct, group },
        },
      };
      changes.push(
        `Copied messages.ackReaction → whatsapp.ackReaction (scope: ${legacyScope}).`,
      );
    }
  }

  return { config: next, changes };
}

export async function maybeMigrateLegacyConfigFile(runtime: RuntimeEnv) {
  const legacyConfigPath = resolveLegacyConfigPath(process.env);
  if (legacyConfigPath === CONFIG_PATH_CLAWDBOT) return;

  const legacyIo = createConfigIO({ configPath: legacyConfigPath });
  const legacySnapshot = await legacyIo.readConfigFileSnapshot();
  if (!legacySnapshot.exists) return;

  const currentSnapshot = await readConfigFileSnapshot();
  if (currentSnapshot.exists) {
    note(
      `Legacy config still exists at ${legacyConfigPath}. Current config at ${CONFIG_PATH_CLAWDBOT}.`,
      "Legacy config",
    );
    return;
  }

  const gatewayMode =
    typeof (legacySnapshot.parsed as ClawdbotConfig)?.gateway?.mode === "string"
      ? (legacySnapshot.parsed as ClawdbotConfig).gateway?.mode
      : undefined;
  const gatewayBind =
    typeof (legacySnapshot.parsed as ClawdbotConfig)?.gateway?.bind === "string"
      ? (legacySnapshot.parsed as ClawdbotConfig).gateway?.bind
      : undefined;
  const parsed = legacySnapshot.parsed as Record<string, unknown>;
  const parsedAgents =
    parsed.agents && typeof parsed.agents === "object"
      ? (parsed.agents as Record<string, unknown>)
      : undefined;
  const parsedDefaults =
    parsedAgents?.defaults && typeof parsedAgents.defaults === "object"
      ? (parsedAgents.defaults as Record<string, unknown>)
      : undefined;
  const parsedLegacyAgent =
    parsed.agent && typeof parsed.agent === "object"
      ? (parsed.agent as Record<string, unknown>)
      : undefined;
  const defaultWorkspace =
    typeof parsedDefaults?.workspace === "string"
      ? parsedDefaults.workspace
      : undefined;
  const legacyWorkspace =
    typeof parsedLegacyAgent?.workspace === "string"
      ? parsedLegacyAgent.workspace
      : undefined;
  const agentWorkspace = defaultWorkspace ?? legacyWorkspace;
  const workspaceLabel = defaultWorkspace
    ? "agents.defaults.workspace"
    : legacyWorkspace
      ? "agent.workspace"
      : "agents.defaults.workspace";

  note(
    [
      `- File exists at ${legacyConfigPath}`,
      gatewayMode ? `- gateway.mode: ${gatewayMode}` : undefined,
      gatewayBind ? `- gateway.bind: ${gatewayBind}` : undefined,
      agentWorkspace ? `- ${workspaceLabel}: ${agentWorkspace}` : undefined,
    ]
      .filter(Boolean)
      .join("\n"),
    "Legacy Clawdis config detected",
  );

  let nextConfig = legacySnapshot.valid ? legacySnapshot.config : null;
  const { config: migratedConfig, changes } = migrateLegacyConfig(
    legacySnapshot.parsed,
  );
  if (migratedConfig) {
    nextConfig = migratedConfig;
  } else if (!nextConfig) {
    note(
      `Legacy config at ${legacyConfigPath} is invalid; skipping migration.`,
      "Legacy config",
    );
    return;
  }

  const normalized = normalizeLegacyConfigValues(nextConfig);
  const mergedChanges = [...changes, ...normalized.changes];
  if (mergedChanges.length > 0) {
    note(mergedChanges.join("\n"), "Doctor changes");
  }

  await writeConfigFile(normalized.config);
  runtime.log(`Migrated legacy config to ${CONFIG_PATH_CLAWDBOT}`);
}
