export interface RegexResourceLimits {
  readonly liveParseDebounceMs: number;
  readonly liveExecutionDebounceMs: number;
  readonly liveExecutionTimeoutMs: number;
  readonly manualExecutionTimeoutMs: number;
  readonly advancedMaximumTimeoutMs: number;
  readonly patternSoftLengthUtf16: number;
  readonly patternHardLengthUtf16: number;
  readonly interactiveSubjectSoftBytes: number;
  readonly interactiveSubjectHardBytes: number;
  readonly corpusSoftBytes: number;
  readonly corpusHardBytes: number;
  readonly maximumCorpusDocuments: number;
  readonly maximumCorpusDocumentBytes: number;
  readonly maximumCorpusLines: number;
  readonly maximumCorpusMatches: number;
  readonly maximumCorpusCaptureSummaries: number;
  readonly maximumCorpusCaptureSamplesPerGroup: number;
  readonly maximumCorpusCaptureSampleUtf16: number;
  readonly maximumCorpusOutputBytes: number;
  readonly maximumCorpusWallTimeMs: number;
  readonly maximumReplacementTemplateUtf16: number;
  readonly maximumListTemplateUtf16: number;
  readonly maximumMatches: number;
  readonly maximumCaptureGroups: number;
  readonly maximumCaptureRows: number;
  readonly maximumReplacementOutputBytes: number;
  readonly maximumTraceEvents: number;
  readonly maximumTraceBytes: number;
  readonly maximumBenchmarkIterations: number;
  readonly maximumBenchmarkWallTimeMs: number;
  readonly maximumGeneratedCases: number;
  readonly maximumMinimizerRuns: number;
  readonly maximumLogBytes: number;
}

export const DEFAULT_REGEX_LIMITS: RegexResourceLimits = {
  liveParseDebounceMs: 120,
  liveExecutionDebounceMs: 220,
  liveExecutionTimeoutMs: 250,
  manualExecutionTimeoutMs: 2_000,
  advancedMaximumTimeoutMs: 10_000,
  patternSoftLengthUtf16: 16 * 1024,
  patternHardLengthUtf16: 64 * 1024,
  interactiveSubjectSoftBytes: 1024 * 1024,
  interactiveSubjectHardBytes: 16 * 1024 * 1024,
  corpusSoftBytes: 64 * 1024 * 1024,
  corpusHardBytes: 256 * 1024 * 1024,
  maximumCorpusDocuments: 256,
  maximumCorpusDocumentBytes: 16 * 1024 * 1024,
  maximumCorpusLines: 100_000,
  maximumCorpusMatches: 100_000,
  maximumCorpusCaptureSummaries: 32,
  maximumCorpusCaptureSamplesPerGroup: 3,
  maximumCorpusCaptureSampleUtf16: 160,
  maximumCorpusOutputBytes: 64 * 1024 * 1024,
  maximumCorpusWallTimeMs: 5 * 60 * 1_000,
  maximumReplacementTemplateUtf16: 64 * 1024,
  maximumListTemplateUtf16: 16 * 1024,
  maximumMatches: 10_000,
  maximumCaptureGroups: 1_000,
  maximumCaptureRows: 100_000,
  maximumReplacementOutputBytes: 64 * 1024 * 1024,
  maximumTraceEvents: 50_000,
  maximumTraceBytes: 10 * 1024 * 1024,
  maximumBenchmarkIterations: 10_000,
  maximumBenchmarkWallTimeMs: 30_000,
  maximumGeneratedCases: 1_000,
  maximumMinimizerRuns: 2_000,
  maximumLogBytes: 2 * 1024 * 1024,
};

export const TEMPLATE_PRESENTATION_LIMITS = {
  replacementEditorMarks: 2_000,
  replacementTokenRows: 500,
  listTokenChips: 250,
  listPreviewRows: 500,
  replacementDiagnostics: 200,
} as const;

export function utf8ByteLength(value: string): number {
  let bytes = 0;
  for (let index = 0; index < value.length; index += 1) {
    const first = value.charCodeAt(index);
    if (first <= 0x7f) {
      bytes += 1;
    } else if (first <= 0x7ff) {
      bytes += 2;
    } else if (first >= 0xd800 && first <= 0xdbff) {
      const second = value.charCodeAt(index + 1);
      if (second >= 0xdc00 && second <= 0xdfff) {
        bytes += 4;
        index += 1;
      } else {
        bytes += 3;
      }
    } else {
      bytes += 3;
    }
  }
  return bytes;
}
