<?php

namespace EesyPHP;

use Exception;
use PDO;
use \Envms\FluentPDO\Query;

class Db {

  /**
   * The PDO object of the database connection
   * @var PDO
   */
  public $pdo;

  /**
   * The PDO object of the database connection
   * @var \Envms\FluentPDO\Query
   */
  public $fpdo;

  /**
   * Date format as returned by database
   * @var string
   */
  protected $date_format = '%Y-%m-%d';

  /**
   * Datetime format as returned by database
   * @var string
   */
  protected $datetime_format = '%Y-%m-%d %H:%M:%S';

  /**
   * Locale for time (as expected by LC_TIME)
   * @var string|null
   */
  protected $locale_time = null;

  /**
   * Connect to database and return FluentPDO Query object
   * @param string $dsn Database DSN
   * @param string|null $user Username (optional)
   * @param string|null $password password (optional)
   * @param string|array $options Connection options (optional)
   * @param string|null $date_format Date format in DB (optional)
   * @param string|null $datetime_format Datetime format in DB (optional)
   * @param string|null $locale_time Locale for time (optional)
   * @return void
   */
  public function __construct($dsn, $user=null, $password=null, $options=null,
                              $date_format=null, $datetime_format=null, $locale_time=null) {
    if (!$dsn) {
      Log :: fatal('Database DSN not configured');
      return;
    }
    if ($date_format) $this -> date_format = $date_format;
    if ($datetime_format) $this -> datetime_format = $datetime_format;
    if ($locale_time) $this -> locale_time = $locale_time;

    try {
      // Connect to database
      $this -> pdo = new PDO($dsn, $user, $password, $options);
      $this -> fpdo = new Query($this -> pdo);

      // Register the debug query handler to log it
      $this -> fpdo -> debug = array('\\EesyPHP\\Db', 'debug_query');

      Log :: trace("DB connection established (DSN: '%s')", $dsn);
    }
    catch(Exception $e) {
      Log :: error("Fail to connect to DB (DSN : '%s') : %s", $dsn, $e->getMessage());
      Log :: fatal(I18n::_('Unable to connect to the database.'));
    }
  }

  /**
   * Debug a query
   * @param \Envms\FluentPDO\Queries\Base $q
   * @return void
   */
  public static function debug_query($q) {
    $msg = "# DB query";
    if ($q->getResult())
        $msg .= sprintf(
            " (%0.3f ms; rows = %d)",
            $q->getExecutionTime() * 1000,
            $q->getResult()->rowCount()
        );
    $msg .= ": ".$q->getQuery();

    $parameters = $q->getParameters();
    if ($parameters) {
        if (is_array($parameters)) {
          $msg .= "\n# Parameters: '" . implode("', '", $parameters) . "'";
        }
        else {  // @phpstan-ignore-line
          $msg .= "\n# Parameters: '" . vardump($parameters) . "'";
        }
    }

    Log :: debug($msg);
  }

  /*
   * Handle date/datetime format
   */
  public function set_locale() {
    if ($this -> locale_time)
      setlocale(LC_TIME, $this -> locale_time);
  }

  public function date2time($date) {
    $this -> set_locale();
    $pdate = strptime($date, $this -> date_format);
    return mktime(
      $pdate['tm_hour'], $pdate['tm_min'], $pdate['tm_sec'],
      $pdate['tm_mon'] + 1, $pdate['tm_mday'], $pdate['tm_year'] + 1900
    );
  }

  public function time2date($time) {
    $this -> set_locale();
    return strftime($this -> date_format, $time);
  }

  public function datetime2time($date) {
    $this -> set_locale();
    $pdate = strptime($date, $this -> datetime_format);
    return mktime(
      $pdate['tm_hour'], $pdate['tm_min'], $pdate['tm_sec'],
      $pdate['tm_mon'] + 1, $pdate['tm_mday'], $pdate['tm_year'] + 1900
    );
  }

  public function time2datetime($time) {
    $this -> set_locale();
    return strftime($this -> datetime_format, $time);
  }

  /**
   * Helper method to format row info
   * @param array<string,mixed> $row The raw row info
   * @param array<string> $datetime_fields List of field in datetime format
   * @param array<string> $date_fields List of field in date format
   * @return array
   */
  public function format_row_info($row, $datetime_fields=null, $date_fields=null) {
    // Convert datetime fields
    if (is_array($datetime_fields))
      foreach($datetime_fields as $field)
        if ($row[$field])
          $row[$field] = $this -> datetime2time($row[$field]);
    // Convert date fields
    if (is_array($date_fields))
      foreach($date_fields as $field)
        if ($row[$field])
          $row[$field] = $this -> date2time($row[$field]);
    return $row;
  }
}
