<?php

/**
 * Provides Base Model Class
 */

namespace BitCode\BitForm\Core\Database;

/**
 * Undocumented class
 */

use WP_Error;

class Model
{
  protected static $table;
  protected static $primary_key;
  protected $app_db;
  protected $table_name;
  protected $db_response;

  /**
   * Undocumented function
   */
  public function __construct()
  {
    global $wpdb;
    $this->app_db = $wpdb;
    $this->table_name = $wpdb->prefix . static::$table;
  }

  /**
   * Undocumented function
   *
   * @return void
   */
  public function insert($data = [])
  {
    if (is_null($data)) {
      return new WP_Error('empty_data', 'Form data is empty');
    }
    $result = $this->app_db->insert(
      $this->table_name,
      $data
    );
    return $this->getResult($result);
  }

  /**
   * Undocumented function
   *
   * @param string $item
   * @param array  $condition
   *
   * @return mixed
   */
  public function get($item = '*', $condition = [], $limit = null, $offset = null, $order_by = null, $order_follow = null)
  {
    static $tableExistsCache = [];
    if (!isset($tableExistsCache[$this->table_name])) {
      $tableExistsCache[$this->table_name] = !is_null(
        $this->app_db->get_var(
          $this->app_db->prepare('SHOW TABLES LIKE %s', $this->table_name)
        )
      );
    }
    if (!$tableExistsCache[$this->table_name]) {
      return [];
    }
    if (\is_array($item)) {
      $column_to_select = implode(',', $item);
    } else {
      $column_to_select = $item;
    }
    $checkCondition = $this->checkCondition($condition);
    if (is_wp_error($checkCondition)) {
      return $checkCondition;
    }
    $order = null;
    if (!\is_null($order_by)) {
      $order_follow = \is_null($order_follow) ? 'ASC' : $order_follow;
      $order .= " ORDER BY $order_by $order_follow";
    }
    $paginate = null;
    if (!\is_null($limit)) {
      $limit = \intval($limit);
      $paginate .= " LIMIT $limit ";
    }
    if (!\is_null($offset)) {
      $offset = \intval($offset);
      $paginate .= " OFFSET  $offset ";
    }
    if (empty($condition)) {
      $sql = "SELECT $column_to_select FROM `$this->table_name` $order $paginate";
      $all_values = null;
    } else {
      $formatted_conditions = $this->getFormatedCondition($condition);
      if ($formatted_conditions) {
        $condition_to_check = $formatted_conditions['conditions'];
        $all_values = $formatted_conditions['values'];
      } else {
        $condition_to_check = null;
        $all_values = null;
      }
      $sql = "SELECT $column_to_select FROM `$this->table_name`"
          . $condition_to_check . $order . $paginate;
    }
    return $this->execute($sql, $all_values)->getResult();
  }

  /**
   * Undocumented function
   *
   * @param string $item
   * @param array  $condition
   *
   * @return void
   */
  public function count($condition = null)
  {
    $checkCondition = $this->checkCondition($condition);
    if (is_wp_error($checkCondition)) {
      return $checkCondition;
    }
    if (empty($condition)) {
      $result = $this->app_db->query(
        "SELECT COUNT(*) FROM `$this->table_name`"
      );
    } else {
      $formatted_conditions = $this->getFormatedCondition($condition);
      if ($formatted_conditions) {
        $condition_to_check = $formatted_conditions['conditions'];
        $all_values = $formatted_conditions['values'];
      } else {
        $condition_to_check = null;
        $all_values = null;
      }
      $result = $this->app_db->query(
        $this->app_db->prepare(
          "SELECT COUNT(*) as count FROM `{$this->table_name}`"
              . $condition_to_check,
          $all_values
        )
      );
    }
    if (!$result) {
      if ($this->app_db->last_error) {
        return new WP_Error('db_error', $this->app_db->last_error);
      }
      return new WP_Error('db_error', 'Result is empty');
    } else {
      return $this->app_db->last_result;
    }
  }

  /**
   * Undocumented function
   *
   * @param array $data_to_update
   * @param array $condition
   *
   * @return void
   */
  public function update(array $data, array $condition)
  {
    if (
      !\is_null($data)
      && \is_array($data)
      && array_keys($data) !== range(0, count($data) - 1)
    ) {
      $data_to_update = $data;
    } else {
      return new WP_Error(
        'update_error',
        'Nothing to update'
      );
    }
    $update_condition = (!\is_null($condition) &&
        array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
    $result = $this->app_db->update(
      $this->table_name,
      $data_to_update,
      $update_condition
    );
    return $this->getResult($result);
  }

  /**
   * Undocumented function
   *
   * @param array $data_to_update
   * @param array $condition
   *
   * @return void
   */
  public function bulkUpdate(array $data = null, array $condition = null)
  {
    if (
      !\is_null($data)
      && \is_array($data)
      && array_keys($data) !== range(0, count($data) - 1)
    ) {
      $data_to_update = $data;
    } else {
      return new WP_Error(
        'update_error',
        'Nothing to update'
      );
    }

    $update_fields = '';
    $all_values = [];
    $index_checker = 0;
    $data_count = count($data_to_update) - 1;
    foreach ($data_to_update as $field_name => $field_value) {
      $update_fields .= $field_name . ' = ' . $this->getFieldFormat($field_value);
      if ($index_checker < $data_count) {
        $update_fields .= ',';
      }
      $index_checker = $index_checker + 1;
      $all_values[] = $field_value;
    }
    $update_condition = (!\is_null($condition) &&
        array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
    $formatted_conditions = $this->getFormatedCondition($update_condition);
    if ($formatted_conditions) {
      $condition_to_check = $formatted_conditions['conditions'];
      $all_values = array_merge($all_values, $formatted_conditions['values']);
    } else {
      $condition_to_check = null;
    }
    $result = $this->app_db->query(
      $this->app_db->prepare(
        "UPDATE `{$this->table_name}` SET $update_fields $condition_to_check",
        $all_values
      )
    );
    return $this->getResult($result);
  }

  /**
   * Duplicate's row
   *
   * @param array $data_to_update
   * @param array $condition
   *
   * @return void
   */
  public function duplicate(array $columns, array $duplicate, array $condition)
  {
    if (!(!\is_null($columns)
        && \is_array($columns)
        && array_keys($columns) === range(0, count($columns) - 1)
        && !\is_null($duplicate)
        && \is_array($duplicate)
        && array_keys($duplicate) === range(0, count($duplicate) - 1))) {
      return new WP_Error(
        'duplicate_error',
        'Nothing to duplicate'
      );
    }

    $dupCol = '';
    $insCol = \implode(',', $columns);
    $all_values = [];
    $data_count = count($duplicate) - 1;
    foreach ($duplicate as $dupKey => $dupColName) {
      if (in_array($dupColName, $columns)) {
        $dupCol .= $dupColName;
      } else {
        $dupCol .= $this->getFieldFormat($dupColName);
        $all_values[] = $dupColName;
      }
      if ($dupKey < $data_count) {
        $dupCol .= ',';
      }
    }
    $condition_to_check = null;
    $update_condition = (!\is_null($condition) &&
        array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
    $formatted_conditions = $this->getFormatedCondition($update_condition);
    if ($formatted_conditions) {
      $condition_to_check = $formatted_conditions['conditions'];
      $all_values = array_merge($all_values, $formatted_conditions['values']);
    }
    $query = "INSERT INTO `{$this->table_name}` ($insCol)
        SELECT $dupCol FROM `{$this->table_name}` $condition_to_check";
    $this->execute($query, $all_values);
    return $this->getResult();
  }

  public function trash(array $condition = null)
  {
    if (
      !\is_null($condition)
      && \is_array($condition)
      && array_keys($condition) !== range(0, count($condition) - 1)
    ) {
      $delete_condition = $condition;
    } else {
      return new WP_Error(
        'deletion_error',
        'At least 1 condition needed'
      );
    }
    $update_condition = (!\is_null($condition) &&
        array_keys($condition) !== range(0, count($condition) - 1)) ? $condition : null;
    $result = $this->app_db->update(
      $this->table_name,
      $data_to_update,
      $update_condition
    );
    return $this->getResult($result);
  }

  public function delete(array $condition = null)
  {
    if (
      !\is_null($condition)
      && \is_array($condition)
      && array_keys($condition) !== range(0, count($condition) - 1)
    ) {
      $delete_condition = $condition;
    } else {
      return new WP_Error(
        'deletion_error',
        'At least 1 condition needed'
      );
    }
    $result = $this->app_db->delete(
      $this->table_name,
      $delete_condition
    );
    return $this->getResult($result);
  }

  public function bulkDelete(array $condition = null)
  {
    if (
      !\is_null($condition)
      && \is_array($condition)
      && array_keys($condition) !== range(0, count($condition) - 1)
    ) {
      $delete_condition = $condition;
    } else {
      return new WP_Error(
        'deletion_error',
        'At least 1 condition needed'
      );
    }
    // $formatted_conditions = $this->getFormatedCondition($delete_condition, $check_operator);
    $formatted_conditions = $this->getFormatedCondition($delete_condition);
    if ($formatted_conditions) {
      $condition_to_check = $formatted_conditions['conditions'];
      $all_values = $formatted_conditions['values'];
    } else {
      $condition_to_check = null;
      return new WP_Error(
        'deletion_error',
        'At least 1 condition needed'
      );
    }
    $result = $this->app_db->query(
      $this->app_db->prepare(
        "DELETE FROM `{$this->table_name}` $condition_to_check",
        $all_values
      )
    );
    return $this->getResult($result);
  }

  protected function getFieldFormat($value)
  {
    return ('integer' === gettype($value)) ?
        '%d' : (('double' === gettype($value)) ? '%f' : '%s');
  }

  protected function getFormatedCondition($condition, $check_operator = null, $join_operator = ' AND ')
  {
    if (\is_null($condition)) {
      return false;
    }
    $no_condition = count($condition);
    $index_checker = 0;
    $condition_to_check = ' WHERE ';
    $all_values = [];
    foreach ($condition as $key => $value) {
      $value_type = '';
      if (is_array($value)) {
        // Check for raw SQL values first
        if (isset($value['raw'])) {
          // Handle raw SQL - don't format or add to prepared values
          $set_check_operator = isset($value['operator']) ? $value['operator'] : '=';
          $value_type = $value['raw']; // Use raw SQL directly
          // Don't add to $all_values since it's raw SQL
        } elseif (isset($value['operator'])) {
          // logic for operator arrays
          $set_check_operator = $value['operator'];
          $value_type .= $this->getFieldFormat($value['value']);
          $all_values[] = $value['value'];
        } else {
          // logic for IN conditions
          $set_check_operator = \is_null($check_operator) ? 'in' : $check_operator;
          $value_type .= ' ( ';
          $value_index_checker = 0;
          $value_count = count($value) - 1;
          foreach ($value as $condKey => $condValue) {
            $value_type .= $this->getFieldFormat($condValue);
            $all_values[] = $condValue;
            if ($value_index_checker < $value_count) {
              $value_type .= ', ';
            }
            $value_index_checker = $value_index_checker + 1;
          }
          $value_type .= ' )';
        }
      } else {
        // logic for simple values
        $set_check_operator = \is_null($check_operator) ? '=' : $check_operator;
        $value_type .= $this->getFieldFormat($value);
        $all_values[] = $value;
      }
      $condition_to_check = $condition_to_check . $key . " $set_check_operator " . $value_type;
      if ($index_checker < $no_condition - 1) {
        $condition_to_check = $condition_to_check . " $join_operator ";
      }
      $index_checker = $index_checker + 1;
    }
    return [
      'conditions' => $condition_to_check,
      'values'     => $all_values
    ];
  }

  protected function checkCondition(array $condition)
  {
    if (!is_null($condition) && array_keys($condition) === range(0, count($condition) - 1)) {
      return new WP_Error(
        'get_condition',
        'Require ASSOC_ARRAY but found N_ARRAY'
      );
    }
    return true;
  }

  protected function execute($sql, $values = null)
  {
    if (is_null($values)) {
      $preparedQuery = $sql;
    } else {
      $preparedQuery = $this->app_db->prepare($sql, $values);
    }
    // echo " Q S " . $preparedQuery . " Q  EE";
    if (empty($preparedQuery)) {
      $this->db_response = new WP_Error('null_query', 'prepared query is empty');
    } else {
      $this->db_response = false !== stripos($preparedQuery, 'DELETE') ? $this->app_db->query($preparedQuery)
          : $this->app_db->get_results($preparedQuery, OBJECT_K);
    }
    // print_r($this->app_db->last_query);
    return $this;
  }

  protected function getResult($db_response = null)
  {
    $db_response = !empty($this->db_response) ? $this->db_response : $db_response;
    if (!empty($this->app_db->last_error)) {
      return new WP_Error('db_error', $this->app_db->last_error);
    }
    if (!$db_response) {
      if ($this->app_db->num_rows > 0) {
        $response = $this->app_db->num_rows;
      }
      if (is_wp_error($db_response)) {
        $response = $db_response;
      }
      $response = new WP_Error('result_empty', 'Result is empty');
    } elseif (is_array($this->app_db->last_result) && !empty($this->app_db->last_result)) {
      $response = $this->app_db->last_result;
    } elseif ($this->app_db->insert_id) {
      $response = $this->app_db->insert_id;
    } else {
      $response = $db_response;
    }
    $this->app_db->flush();
    return $response;
  }

  /**
   * Get last inserted id
   *
   * @return int
   */
  public function lastId()
  {
    $sql = "SELECT id FROM `{$this->table_name}`
    ORDER BY id DESC LIMIT 1";
    $result = $this->execute($sql)->getResult();
    if (is_wp_error($result)) {
      return 0;
    }
    return $result[0]->id;
  }
}
