// src/install/blocks/EnvVarInstaller.ts
import { existsSync, readFileSync } from "node:fs";
import type { StepReceipt } from "../Installer";
import { atomicWrite } from "./atomicWrite";

export type EnvVarReceipt = Extract<StepReceipt, { kind: "env-var" }>;

export interface EnvVarPlan {
  shellRc: string;
  linesToInsert: string[];
  insertAfterLine: number;
}

export class EnvVarInstaller {
  private readonly fence: { open: string; close: string };

  constructor(
    private readonly shellRcs: string[],
    private readonly name: string,
    private readonly value: string,
    private readonly installerId: string,
  ) {
    this.fence = {
      open: `# >>> fewtok install: ${installerId} >>>`,
      close: `# <<< fewtok install: ${installerId} <<<`,
    };
  }

  private get rcPath(): string {
    // Pick the first rc that exists, else first in list
    return this.shellRcs.find((p) => existsSync(p)) ?? this.shellRcs[0]!;
  }

  async plan(): Promise<EnvVarPlan> {
    const rc = this.rcPath;
    const lines = existsSync(rc) ? readFileSync(rc, "utf8").split("\n") : [];
    const block = [
      this.fence.open,
      `export ${this.name}='${this.value}'`,
      this.fence.close,
    ];
    return { shellRc: rc, linesToInsert: block, insertAfterLine: lines.length };
  }

  async apply(plan: EnvVarPlan): Promise<EnvVarReceipt> {
    const existing = existsSync(plan.shellRc) ? readFileSync(plan.shellRc, "utf8") : "";
    // Remove any pre-existing fence for this installer
    const cleaned = this.removeFence(existing);
    const appended = cleaned.trimEnd() + "\n" + plan.linesToInsert.join("\n") + "\n";
    await atomicWrite(plan.shellRc, appended);
    // Line range of inserted block
    const originalLines = cleaned.trimEnd().split("\n").length;
    const endLine = originalLines + plan.linesToInsert.length;
    return {
      kind: "env-var",
      shellRc: plan.shellRc,
      lineRangeAdded: [originalLines + 1, endLine],
    };
  }

  async revert(receipt: EnvVarReceipt): Promise<void> {
    if (!existsSync(receipt.shellRc)) return;
    const text = readFileSync(receipt.shellRc, "utf8");
    const cleaned = this.removeFence(text);
    await atomicWrite(receipt.shellRc, cleaned);
  }

  private removeFence(text: string): string {
    const openIdx = text.indexOf(this.fence.open);
    const closeIdx = text.indexOf(this.fence.close);
    if (openIdx === -1 || closeIdx === -1) return text;
    // Find start of line for openIdx
    const before = text.slice(0, openIdx).replace(/\n$/, "");
    const after = text.slice(closeIdx + this.fence.close.length).replace(/^\n/, "");
    return before + (after.length > 0 ? "\n" + after : "\n");
  }
}
