import type { OcrCheckpoint } from "./checkpoints.ts";
import type { OcrBlock } from "./layout.ts";

export interface OcrProgress {
  completedPages: number;
  totalPages: number;
  currentPage: number;
  status: "recognizing" | "paused" | "complete" | "failed";
}

interface CheckpointPort {
  get(
    bookId: string,
    sourceFingerprint: string,
    pageNumber: number,
  ): Promise<OcrCheckpoint | null>;
  save(checkpoint: OcrCheckpoint): Promise<void>;
}

interface RecognizerPort<Image> {
  recognize(
    image: Image,
    pageNumber: number,
  ): Promise<{ width: number; height: number; blocks: OcrBlock[] }>;
  terminate(): Promise<void>;
}

export function createOcrJob<Image>(dependencies: {
  bookId: string;
  sourceFingerprint: string;
  pageCount: number;
  checkpoints: CheckpointPort;
  renderer(pageNumber: number): Promise<{ pageNumber: number; bitmap: Image }>;
  recognizer: RecognizerPort<Image>;
  now?: () => Date;
}) {
  if (!Number.isInteger(dependencies.pageCount) || dependencies.pageCount < 1) {
    throw new Error("OCR 页数必须是正整数。");
  }
  let pauseRequested = false;
  let running = false;
  const now = dependencies.now ?? (() => new Date());

  return {
    pause() {
      pauseRequested = true;
    },

    async run(
      onProgress: (progress: OcrProgress) => void = () => undefined,
    ): Promise<OcrCheckpoint[]> {
      if (running) throw new Error("OCR 作业已经在运行。");
      running = true;
      pauseRequested = false;
      const results: OcrCheckpoint[] = [];
      try {
        for (let pageNumber = 1; pageNumber <= dependencies.pageCount; pageNumber += 1) {
          const existing = await dependencies.checkpoints.get(
            dependencies.bookId,
            dependencies.sourceFingerprint,
            pageNumber,
          );
          if (existing) {
            results.push(existing);
          } else {
            const rendered = await dependencies.renderer(pageNumber);
            const recognition = await dependencies.recognizer.recognize(
              rendered.bitmap,
              pageNumber,
            );
            const checkpoint: OcrCheckpoint = {
              bookId: dependencies.bookId,
              sourceFingerprint: dependencies.sourceFingerprint,
              pageNumber,
              width: recognition.width,
              height: recognition.height,
              blocks: recognition.blocks,
              completedAt: now().toISOString(),
            };
            await dependencies.checkpoints.save(checkpoint);
            results.push(checkpoint);
          }

          const status = pauseRequested
            ? "paused"
            : results.length === dependencies.pageCount
              ? "complete"
              : "recognizing";
          onProgress({
            completedPages: results.length,
            totalPages: dependencies.pageCount,
            currentPage: pageNumber,
            status,
          });
          if (pauseRequested) break;
        }
        return results;
      } catch (error) {
        onProgress({
          completedPages: results.length,
          totalPages: dependencies.pageCount,
          currentPage: Math.min(results.length + 1, dependencies.pageCount),
          status: "failed",
        });
        throw error;
      } finally {
        running = false;
        await dependencies.recognizer.terminate();
      }
    },
  };
}
