import * as vscode from 'vscode';
import { readFile, existsSync } from 'fs';
import { join } from 'path';

type Config = {
  bibTeXFilePath: string;
};

export class BibTeXCitations {
  static _rxBibTeXInNote = /(?<= |,|^|\[|\[-)@[\p{L}\d\-_]+(?!\(.\S\))/giu;
  static _rxBibTeXInLibrary = /^@\S+{(\S+),/gm;

  static cfg(): Config {
    let c = vscode.workspace.getConfiguration('vscodeMarkdownNotes');
    return {
      bibTeXFilePath: c.get('bibtexFilePath') as string,
    };
  }

  static isBibtexFileConfigured(): Boolean {
    return existsSync(this.bibTexFilePath());
  }

  static rxBibTeX(): RegExp {
    return this._rxBibTeXInNote;
  }

  static async citations(): Promise<Array<string>> {
    return this.bibTeXFile().then((x) => this.parseCitations(x));
  }

  static async location(citation: string): Promise<vscode.Location> {
    return this.bibTeXFile().then((x) => {
      const pos = this.position(x, citation);
      if (pos == null) {
        return Promise.reject('Cannot get location');
      } else {
        const uri = vscode.Uri.file(this.bibTexFilePath());
        return new vscode.Location(uri, pos);
      }
    });
  }

  private static bibTexFilePath(): string {
    const path = this.cfg().bibTeXFilePath;

    // Absolute path (Unix and Windows)
    if (path.startsWith('/') || path.indexOf(':\\') == 1) {
      return path;
    }

    // Workspace relative path
    const folders = vscode.workspace?.workspaceFolders;
    if (folders && folders.length > 0) {
      return join(folders[0].uri.fsPath.toString(), path);
    }
    return path;
  }

  private static bibTeXFile(): Promise<string> {
    const path = this.bibTexFilePath();
    if (path == null || path == '') {
      return Promise.reject('BibTeX file location not set');
    }

    return new Promise((resolve, reject) => {
      readFile(path, (error, buffer) => {
        if (error) {
          reject(error);
        } else {
          resolve(buffer.toString());
        }
      });
    });
  }

  private static parseCitations(data: string): Array<string> {
    let matches = data.matchAll(this._rxBibTeXInLibrary);
    return Array.from(matches).map((x) => x[1]);
  }

  private static position(data: string, citation: string): vscode.Position | null {
    let pos = data.match(citation)?.index;
    if (pos == null) {
      return null;
    }
    const numLines = data.substring(0, pos).split('\n').length;
    return new vscode.Position(numLines - 1, 0);
  }
}