#!/usr/bin/env python3

import random
import subprocess

from runner import run_sql_parallel
from sqlite_static_helper import *
from stats import save_bug


def is_crash(rc: int, _stderr: str) -> bool:

  # https://tldp.org/LDP/abs/html/exitcodes.html
  if rc > 128 and rc != 130:
    return True

  return False


def is_unexpected_error(b_rc, _b_err, r_rc, _r_err, _sql):
  if b_rc != 0 and r_rc == 0:
    return True
  return False


def is_valid_sql(r_rc: int, _r_err: str):
  if r_rc != 0:
    return False
  return True


def check(buggy_bin: str,
          ref_bin: str,
          sql: str,
          with_flag: bool = False,
          save_bugs: bool = True) -> int:
  """
  Returns:
  - `-1`: The query was not successfully executed on the reference binary, probably due to syntax issues
  - `0`: The query caused a logic bug, crash, or some other unexpected error.
  - `1`: The query was successfully executed on both binaries.
  """

  flag = '' if not with_flag else random.choice(FLAGS)

  try:
    b_out, b_err, b_rc, r_out, r_err, r_rc = run_sql_parallel(
      buggy_bin, ref_bin, sql, flag)
  except subprocess.TimeoutExpired:
    return 0
  except Exception:
    return -1

  if not is_valid_sql(r_rc, r_err):
    return -1

  # # Crash Detection
  if is_crash(b_rc, b_err) and not is_crash(r_rc, r_err):
    if save_bugs:
      save_bug(sql, 'crash', b_out, b_err, flag=flag)
    return 0

  # # Unexpected Error Detection
  elif is_unexpected_error(b_rc, b_err, r_rc, r_err, sql):
    if save_bugs:
      save_bug(sql, 'unexpected_error', b_out, b_err, flag=flag)
    return 0

  # # Logic Bug Detection
  elif b_rc == 0 and r_rc == 0 and b_out != r_out:
    if save_bugs:
      save_bug(sql, 'logic', b_out, b_err, r_out, flag=flag)
    return 0
  return 1
