// @generated — DO NOT EDIT. Source: packages/shared/storage.ts
/**
 * Plan Storage Utility
 *
 * Saves plans and annotations to ~/.plannotator/plans/
 * Cross-platform: works on Windows, macOS, and Linux.
 *
 * Runtime-agnostic: uses only node:fs, node:path, node:os.
 */

import { homedir } from "os";
import { join, resolve, sep } from "path";
import { mkdirSync, writeFileSync, readFileSync, readdirSync, statSync, existsSync } from "fs";
import { sanitizeTag } from "./project";
import { resolveUserPath } from "./resolve-file";

/**
 * Get the plan storage directory, creating it if needed.
 * Cross-platform: uses os.homedir() for Windows/macOS/Linux compatibility.
 * @param customPath Optional custom path. Supports ~ for home directory.
 */
export function getPlanDir(customPath?: string | null): string {
  let planDir: string;

  if (customPath?.trim()) {
    planDir = resolveUserPath(customPath);
  } else {
    planDir = join(homedir(), ".plannotator", "plans");
  }

  mkdirSync(planDir, { recursive: true });
  return planDir;
}

/**
 * Extract the first heading from markdown content.
 */
function extractFirstHeading(markdown: string): string | null {
  const match = markdown.match(/^#\s+(.+)$/m);
  if (!match) return null;
  return match[1].trim();
}

/**
 * Generate a slug from plan content.
 * Format: {sanitized-heading}-YYYY-MM-DD
 */
export function generateSlug(plan: string): string {
  const date = new Date().toISOString().split("T")[0]; // YYYY-MM-DD

  const heading = extractFirstHeading(plan);
  const slug = heading ? sanitizeTag(heading) : null;

  return slug ? `${slug}-${date}` : `plan-${date}`;
}

/**
 * Save the plan markdown to disk.
 * Returns the full path to the saved file.
 */
export function savePlan(slug: string, content: string, customPath?: string | null): string {
  const planDir = getPlanDir(customPath);
  const filePath = join(planDir, `${slug}.md`);
  writeFileSync(filePath, content, "utf-8");
  return filePath;
}

/**
 * Save annotations to disk.
 * Returns the full path to the saved file.
 */
export function saveAnnotations(slug: string, annotationsContent: string, customPath?: string | null): string {
  const planDir = getPlanDir(customPath);
  const filePath = join(planDir, `${slug}.annotations.md`);
  writeFileSync(filePath, annotationsContent, "utf-8");
  return filePath;
}

/**
 * Save the final snapshot on approve/deny.
 * Combines plan and annotations into a single file with status suffix.
 * Returns the full path to the saved file.
 */
export function saveFinalSnapshot(
  slug: string,
  status: "approved" | "denied",
  plan: string,
  annotations: string,
  customPath?: string | null
): string {
  const planDir = getPlanDir(customPath);
  const filePath = join(planDir, `${slug}-${status}.md`);

  // Combine plan with annotations appended
  let content = plan;
  if (annotations && annotations !== "No changes detected.") {
    content += "\n\n---\n\n" + annotations;
  }

  writeFileSync(filePath, content, "utf-8");
  return filePath;
}

// --- Plan Archive ---

export interface ArchivedPlan {
  filename: string;
  title: string;
  date: string;
  timestamp: string;  // ISO string from file mtime
  status: "approved" | "denied" | "unknown";
  size: number;
}

/**
 * Parse an archive filename into metadata.
 * Handles both old (DATE-heading-status.md) and new (heading-DATE-status.md) formats.
 */
export function parseArchiveFilename(filename: string): ArchivedPlan | null {
  // Skip non-decision files
  if (filename.endsWith(".annotations.md") || filename.endsWith(".diff.md")) return null;

  const base = filename.replace(/\.md$/, "");

  // Extract status suffix
  let status: ArchivedPlan["status"] = "unknown";
  let slug = base;
  if (base.endsWith("-approved")) {
    status = "approved";
    slug = base.slice(0, -"-approved".length);
  } else if (base.endsWith("-denied")) {
    status = "denied";
    slug = base.slice(0, -"-denied".length);
  } else {
    // Skip plain files (no decision status)
    return null;
  }

  // Extract date (YYYY-MM-DD) — could be anywhere in the slug
  const dateMatch = slug.match(/(\d{4}-\d{2}-\d{2})/);
  const date = dateMatch ? dateMatch[1] : "";

  // Title: remove date, convert hyphens to spaces, trim
  const title = slug
    .replace(/\d{4}-\d{2}-\d{2}/, "")
    .replace(/^-+|-+$/g, "")
    .replace(/-+/g, " ")
    .trim() || "Untitled Plan";

  return { filename, title, date, timestamp: "", status, size: 0 };
}

/**
 * List all archived plans (approved/denied decision snapshots).
 * Returns plans sorted by date descending.
 */
export function listArchivedPlans(customPath?: string | null): ArchivedPlan[] {
  const planDir = getPlanDir(customPath);
  try {
    const entries = readdirSync(planDir);
    const plans: ArchivedPlan[] = [];
    for (const entry of entries) {
      if (!entry.endsWith(".md")) continue;
      const parsed = parseArchiveFilename(entry);
      if (!parsed) continue;
      try {
        const stat = statSync(join(planDir, entry));
        parsed.size = stat.size;
        parsed.timestamp = stat.mtime.toISOString();
      } catch { /* keep defaults */ }
      plans.push(parsed);
    }
    return plans.sort((a, b) => b.date.localeCompare(a.date) || b.timestamp.localeCompare(a.timestamp));
  } catch {
    return [];
  }
}

/**
 * Read an archived plan file by filename.
 * Returns null if the file doesn't exist or on read error.
 */
export function readArchivedPlan(filename: string, customPath?: string | null): string | null {
  const planDir = getPlanDir(customPath);
  const filePath = resolve(planDir, filename);
  // Guard against path traversal (resolve + trailing separator, matching reference-handlers.ts)
  if (!filePath.startsWith(planDir + sep)) return null;
  try {
    return readFileSync(filePath, "utf-8");
  } catch {
    return null;
  }
}

// --- Version History ---

/**
 * Get the history directory for a project/slug combination, creating it if needed.
 * History is always stored in ~/.plannotator/history/{project}/{slug}/.
 * Not affected by the customPath setting (that only affects decision saves).
 */
export function getHistoryDir(project: string, slug: string): string {
  const historyDir = join(homedir(), ".plannotator", "history", project, slug);
  mkdirSync(historyDir, { recursive: true });
  return historyDir;
}

/**
 * Determine the next version number by scanning existing files.
 * Returns 1 if no versions exist, otherwise max + 1.
 */
function getNextVersionNumber(historyDir: string): number {
  try {
    const entries = readdirSync(historyDir);
    let max = 0;
    for (const entry of entries) {
      const match = entry.match(/^(\d+)\.md$/);
      if (match) {
        const num = parseInt(match[1], 10);
        if (num > max) max = num;
      }
    }
    return max + 1;
  } catch {
    return 1;
  }
}

/**
 * Save a plan version to the history directory.
 * Deduplication: if the latest version has identical content, skip saving.
 * Returns the version number, file path, and whether a new file was created.
 */
export function saveToHistory(
  project: string,
  slug: string,
  plan: string
): { version: number; path: string; isNew: boolean } {
  const historyDir = getHistoryDir(project, slug);
  const nextVersion = getNextVersionNumber(historyDir);

  // Deduplicate: check if latest version has identical content
  if (nextVersion > 1) {
    const latestPath = join(historyDir, `${String(nextVersion - 1).padStart(3, "0")}.md`);
    try {
      const existing = readFileSync(latestPath, "utf-8");
      if (existing === plan) {
        return { version: nextVersion - 1, path: latestPath, isNew: false };
      }
    } catch {
      // File read failed, proceed with saving
    }
  }

  const fileName = `${String(nextVersion).padStart(3, "0")}.md`;
  const filePath = join(historyDir, fileName);
  writeFileSync(filePath, plan, "utf-8");
  return { version: nextVersion, path: filePath, isNew: true };
}

/**
 * Read a specific version's content from history.
 * Returns null if the version doesn't exist or on read error.
 */
export function getPlanVersion(
  project: string,
  slug: string,
  version: number
): string | null {
  const historyDir = join(homedir(), ".plannotator", "history", project, slug);
  const fileName = `${String(version).padStart(3, "0")}.md`;
  const filePath = join(historyDir, fileName);

  try {
    return readFileSync(filePath, "utf-8");
  } catch {
    return null;
  }
}

/**
 * Get the file path for a specific version in history.
 * Returns null if the version file doesn't exist.
 */
export function getPlanVersionPath(
  project: string,
  slug: string,
  version: number
): string | null {
  const historyDir = join(homedir(), ".plannotator", "history", project, slug);
  const fileName = `${String(version).padStart(3, "0")}.md`;
  const filePath = join(historyDir, fileName);
  return existsSync(filePath) ? filePath : null;
}

/**
 * Get the number of versions stored for a project/slug.
 * Returns 0 if the directory doesn't exist.
 */
export function getVersionCount(project: string, slug: string): number {
  const historyDir = join(homedir(), ".plannotator", "history", project, slug);
  try {
    const entries = readdirSync(historyDir);
    return entries.filter((e) => /^\d+\.md$/.test(e)).length;
  } catch {
    return 0;
  }
}

/**
 * List all versions for a project/slug with metadata.
 * Returns versions sorted ascending by version number.
 */
export function listVersions(
  project: string,
  slug: string
): Array<{ version: number; timestamp: string }> {
  const historyDir = join(homedir(), ".plannotator", "history", project, slug);
  try {
    const entries = readdirSync(historyDir);
    const versions: Array<{ version: number; timestamp: string }> = [];
    for (const entry of entries) {
      const match = entry.match(/^(\d+)\.md$/);
      if (match) {
        const version = parseInt(match[1], 10);
        const filePath = join(historyDir, entry);
        try {
          const stat = statSync(filePath);
          versions.push({ version, timestamp: stat.mtime.toISOString() });
        } catch {
          versions.push({ version, timestamp: "" });
        }
      }
    }
    return versions.sort((a, b) => a.version - b.version);
  } catch {
    return [];
  }
}

/**
 * List all plan slugs stored for a project.
 * Returns slugs sorted by most recently modified first.
 */
export function listProjectPlans(
  project: string
): Array<{ slug: string; versions: number; lastModified: string }> {
  const projectDir = join(homedir(), ".plannotator", "history", project);
  try {
    const entries = readdirSync(projectDir, { withFileTypes: true });
    const plans: Array<{ slug: string; versions: number; lastModified: string }> = [];
    for (const entry of entries) {
      if (!entry.isDirectory()) continue;
      const slugDir = join(projectDir, entry.name);
      const files = readdirSync(slugDir).filter((f) => /^\d+\.md$/.test(f));
      if (files.length === 0) continue;

      // Find most recent file modification time
      let latest = 0;
      for (const file of files) {
        try {
          const mtime = statSync(join(slugDir, file)).mtime.getTime();
          if (mtime > latest) latest = mtime;
        } catch { /* skip */ }
      }

      plans.push({
        slug: entry.name,
        versions: files.length,
        lastModified: latest ? new Date(latest).toISOString() : "",
      });
    }
    return plans.sort((a, b) => b.lastModified.localeCompare(a.lastModified));
  } catch {
    return [];
  }
}
