import { Transform, TransformCallback, TransformOptions } from 'stream';

export default class MySQLParser extends Transform {
   private _buffer: string;
   private _lastChar: string;
   private _last9Chars: string;

   encoding: BufferEncoding;
   delimiter: string;
   isEscape: boolean;
   currentQuote: string;
   isDelimiter: boolean;

   constructor (opts?: TransformOptions & { delimiter: string }) {
      opts = {
         delimiter: ';',
         encoding: 'utf8',
         writableObjectMode: true,
         readableObjectMode: true,
         ...opts
      };
      super(opts);
      this._buffer = '';
      this._lastChar = '';
      this._last9Chars = '';
      this.encoding = opts.encoding;
      this.delimiter = opts.delimiter;

      this.isEscape = false;
      this.currentQuote = null;
      this.isDelimiter = false;
   }

   _transform (chunk: Buffer, encoding: BufferEncoding, next: TransformCallback) {
      for (const char of chunk.toString(this.encoding)) {
         this.checkEscape();
         this._buffer += char;
         this._lastChar = char;
         this._last9Chars += char.trim().toLocaleLowerCase();

         if (this._last9Chars.length > 9)
            this._last9Chars = this._last9Chars.slice(-9);

         this.checkNewDelimiter(char);
         this.checkQuote(char);
         const query = this.getQuery();

         if (query)
            this.push(query);
      }
      next();
   }

   checkEscape () {
      if (this._buffer.length > 0) {
         this.isEscape = this._lastChar === '\\'
            ? !this.isEscape
            : false;
      }
   }

   checkNewDelimiter (char: string) {
      if (this.currentQuote === null && this._last9Chars === 'delimiter') {
         this.isDelimiter = true;
         this._buffer = '';
      }
      else {
         const isNewLine = char === '\n' || char === '\r';
         if (isNewLine && this.isDelimiter) {
            this.isDelimiter = false;
            this.delimiter = this._buffer.trim();
            this._buffer = '';
         }
      }
   }

   checkQuote (char: string) {
      const isQuote = !this.isEscape && (char === '\'' || char === '"');
      if (isQuote && this.currentQuote === char)
         this.currentQuote = null;

      else if (isQuote && this.currentQuote === null)
         this.currentQuote = char;
   }

   getQuery () {
      if (this.isDelimiter)
         return false;

      let query: false | string = false;
      let demiliterFound = false;
      if (this.currentQuote === null && this._buffer.length >= this.delimiter.length)
         demiliterFound = this._last9Chars.slice(-this.delimiter.length) === this.delimiter;

      if (demiliterFound) {
         const parsedStr = this._buffer.trim();
         query = parsedStr.slice(0, parsedStr.length - this.delimiter.length);
         this._buffer = '';
      }

      return query;
   }
}
