import fs from "node:fs/promises";
import path from "node:path";
import type { ReviewSampleListResult, ReviewSamplesRepositoryPort, ReviewSamplePayload } from "./contracts.js";
import type { ReviewSampleRecord, SaveResultWithPath } from "../../src/types/global.js";

const SMALL_FILE_LIMIT_BYTES = 8 * 1024 * 1024;
const TAIL_READ_LIMIT_BYTES = 32 * 1024 * 1024;

export class ReviewSamplesRepository implements ReviewSamplesRepositoryPort {
  private readonly filePath: string;

  constructor(userDataPath: string, fileName = "review-samples.jsonl") {
    this.filePath = path.join(userDataPath, fileName);
  }

  async list(limit = 50): Promise<ReviewSampleListResult> {
    try {
      const safeLimit = Math.max(1, Math.min(200, Number(limit) || 50));
      const stats = await fs.stat(this.filePath);
      const raw = stats.size <= SMALL_FILE_LIMIT_BYTES
        ? await fs.readFile(this.filePath, "utf8")
        : await readTailText(this.filePath, stats.size, TAIL_READ_LIMIT_BYTES);
      const lines = raw.split(/\r?\n/).filter(Boolean);
      const samples = lines
        .slice(-safeLimit)
        .map((line) => {
          try {
            return JSON.parse(line) as ReviewSampleRecord;
          } catch {
            return null;
          }
        })
        .filter(Boolean) as ReviewSampleRecord[];
      const total = stats.size <= SMALL_FILE_LIMIT_BYTES ? lines.length : Math.max(samples.length, lines.length);
      return { ok: true, samples: samples.reverse(), total, path: this.filePath };
    } catch {
      return { ok: true, samples: [], total: 0, path: this.filePath };
    }
  }

  async append(sample: ReviewSamplePayload): Promise<SaveResultWithPath> {
    await fs.mkdir(path.dirname(this.filePath), { recursive: true });
    await fs.appendFile(this.filePath, `${JSON.stringify({ savedAt: new Date().toISOString(), sample })}\n`, "utf8");
    return { ok: true, path: this.filePath };
  }
}

async function readTailText(filePath: string, fileSize: number, maxBytes: number) {
  const bytesToRead = Math.min(fileSize, maxBytes);
  const handle = await fs.open(filePath, "r");
  try {
    const buffer = Buffer.alloc(bytesToRead);
    await handle.read(buffer, 0, bytesToRead, fileSize - bytesToRead);
    const text = buffer.toString("utf8");
    const firstNewline = text.indexOf("\n");
    return fileSize > bytesToRead && firstNewline >= 0 ? text.slice(firstNewline + 1) : text;
  } finally {
    await handle.close();
  }
}
