import { streamLines } from "../../../diff/util"
import { HelperVars } from "../../util/HelperVars"

import { stopAtStartOf, stopAtStopTokens } from "./charStream"
import {
  avoidEmptyComments,
  avoidPathLine,
  noDoubleNewLine,
  showWhateverWeHaveAtXMs,
  skipPrefixes,
  stopAtLines,
  stopAtLinesExact,
  stopAtRepeatingLines,
  stopAtSimilarLine,
  streamWithNewLines,
} from "./lineStream"

const STOP_AT_PATTERNS = ["diff --git"]

export class StreamTransformPipeline {
  async *transform(
    generator: AsyncGenerator<string>,
    prefix: string,
    suffix: string,
    multiline: boolean,
    stopTokens: string[],
    fullStop: () => void,
    helper: HelperVars,
  ): AsyncGenerator<string> {
    let charGenerator = generator

    charGenerator = stopAtStopTokens(generator, [...stopTokens, ...STOP_AT_PATTERNS])
    charGenerator = stopAtStartOf(charGenerator, suffix)
    for (const charFilter of helper.lang.charFilters ?? []) {
      charGenerator = charFilter({
        chars: charGenerator,
        prefix,
        suffix,
        filepath: helper.filepath,
        multiline,
      })
    }

    let lineGenerator = streamLines(charGenerator)

    lineGenerator = stopAtLines(lineGenerator, fullStop)
    const lineBelowCursor = this.getLineBelowCursor(helper)
    if (lineBelowCursor.trim() !== "") {
      lineGenerator = stopAtLinesExact(lineGenerator, fullStop, [lineBelowCursor])
    }
    lineGenerator = stopAtRepeatingLines(lineGenerator, fullStop)
    lineGenerator = avoidEmptyComments(lineGenerator, helper.lang.singleLineComment)
    lineGenerator = avoidPathLine(lineGenerator, helper.lang.singleLineComment)
    lineGenerator = skipPrefixes(lineGenerator)
    lineGenerator = noDoubleNewLine(lineGenerator)

    for (const lineFilter of helper.lang.lineFilters ?? []) {
      lineGenerator = lineFilter({ lines: lineGenerator, fullStop })
    }

    lineGenerator = stopAtSimilarLine(lineGenerator, this.getLineBelowCursor(helper), fullStop)

    const timeoutValue = helper.options.modelTimeout

    lineGenerator = showWhateverWeHaveAtXMs(lineGenerator, timeoutValue!)

    const finalGenerator = streamWithNewLines(lineGenerator)
    for await (const update of finalGenerator) {
      yield update
    }
  }

  private getLineBelowCursor(helper: HelperVars): string {
    let lineBelowCursor = ""
    let i = 1
    while (lineBelowCursor.trim() === "" && helper.pos.line + i <= helper.fileLines.length - 1) {
      lineBelowCursor = helper.fileLines[Math.min(helper.pos.line + i, helper.fileLines.length - 1)]
      i++
    }
    return lineBelowCursor
  }
}
