// eslint-disable-next-line no-restricted-imports
import { promises as fs } from "fs";
import { chalkStderr } from "chalk";
import { logMessage } from "../../../bundler/log.js";
import { hashSha256 } from "../utils/hash.js";

export function isInInteractiveTerminal(): boolean {
  return process.stdin.isTTY === true;
}

export async function readFileSafe(filePath: string): Promise<string | null> {
  try {
    return await fs.readFile(filePath, "utf8");
  } catch {
    return null;
  }
}

/**
 * Attempt to delete a file. Returns `true` if the file was deleted,
 * `false` if it didn't exist or the deletion failed.
 */
export async function safelyDeleteFile(filePath: string): Promise<boolean> {
  try {
    await fs.unlink(filePath);
    return true;
  } catch {
    return false;
  }
}

// ---------------------------------------------------------------------------
// Generic managed-section helpers
//
// Several files (AGENTS.md, CLAUDE.md) contain a Convex-managed section
// delimited by start/end markers. These helpers provide the common logic
// for injecting, stripping, and detecting those sections.
// ---------------------------------------------------------------------------

export type ManagedSectionTarget = {
  filePath: string;
  startMarker: string;
  endMarker: string;
};

export type InjectResult = {
  sectionHash: string;
  didWrite: boolean;
};

export const iife = <T>(fn: () => T): T => fn();

/**
 * Inject a managed section into a file. If the file already contains the
 * markers, the section between them is replaced. Otherwise the section is
 * appended (or the file is created). Only writes when content actually
 * changes.
 */
export async function injectManagedSection(
  opts: ManagedSectionTarget & { section: string },
): Promise<InjectResult> {
  const { filePath, startMarker, endMarker, section } = opts;

  const existing = (await readFileSafe(filePath)) ?? "";

  const startIdx = existing.indexOf(startMarker);
  const endIdx = existing.indexOf(endMarker);

  const updated = iife(() => {
    if (startIdx !== -1 && endIdx !== -1)
      return (
        existing.slice(0, startIdx) +
        section +
        existing.slice(endIdx + endMarker.length)
      );
    if (existing.length > 0)
      return existing.trimEnd() + "\n\n" + section + "\n";

    return section + "\n";
  });

  const didWrite = updated !== existing;
  if (didWrite) await fs.writeFile(filePath, updated, "utf8");

  return { sectionHash: hashSha256(section), didWrite };
}

export type StripResult = "none" | "section" | "file";

/**
 * Remove the managed section (between start/end markers) from a file.
 * If the file is empty after removal, it is deleted.
 *
 * Returns `"none"` if the file doesn't exist or has no markers,
 * `"section"` if the section was stripped, or `"file"` if the entire
 * file was deleted.
 */
export async function stripManagedSection(
  opts: ManagedSectionTarget,
): Promise<StripResult> {
  const { filePath, startMarker, endMarker } = opts;

  const content = await readFileSafe(filePath);
  if (content === null) return "none";

  const startIdx = content.indexOf(startMarker);
  const endIdx = content.indexOf(endMarker);
  if (startIdx === -1 || endIdx === -1) {
    return "none";
  }

  const before = content.slice(0, startIdx).trimEnd();
  const after = content.slice(endIdx + endMarker.length).trimStart();
  const updated = [before, after].filter(Boolean).join("\n\n");

  if (!updated.trim()) {
    await safelyDeleteFile(filePath);
    return "file";
  }

  await fs.writeFile(filePath, updated + "\n", "utf8");
  return "section";
}

export async function removeMarkdownSection({
  projectDir,
  strip,
  fileName,
}: {
  projectDir: string;
  strip: (dir: string) => Promise<StripResult>;
  fileName: string;
}): Promise<boolean> {
  const result = await strip(projectDir);

  if (result === "section") {
    logMessage(
      `${chalkStderr.green("✔")} Removed Convex section from ${fileName}.`,
    );
    return true;
  }

  if (result === "file") {
    logMessage(`${chalkStderr.green("✔")} Deleted ${fileName}.`);
    return true;
  }

  return false;
}

/**
 * Check whether a file contains a managed section (both markers present).
 */
export async function hasManagedSection(
  opts: ManagedSectionTarget,
): Promise<boolean> {
  const content = await readFileSafe(opts.filePath);
  return (
    content !== null &&
    content.includes(opts.startMarker) &&
    content.includes(opts.endMarker)
  );
}
