<?php
// namespace TestSuite\Checks;

// use ISO8583\Message;
// use TestSuite\Settings;
// use TestSuite\Connection;

/**
 * AbstractRequest
 */
abstract class AbstractRequest
{
  protected $protocol;
  protected $config;
  protected $request;
  protected $expectation;
	protected $connection;
	protected $persistent;

  /**
   * New check constructor
   */
  public function __construct($config, $protocol, $persistent = true, $connection = null)
  {
    $this->protocol = $protocol;
    $this->config   = $config;
		$this->persistent = $persistent;

		if ($connection === null) {
			$this->setConnection();
		} else {
			$this->connection = $connection;
		}
  }

  /**
   * Returns empty ISO8583 Message
   *
   * @return \ISO8583\Message ISO8583 Message object
   */
  protected function getEmptyMessage()
  {
    return new Message($this->protocol, [
      'lengthPrefix' => 0
    ]);
  }

  protected function getHeaderMessage()
  {
    $message = $this->getEmptyMessage();
    //设置 TUDU头(10位) => ID(60H)+目的地址(N2)+源地址(N2) 60 0016 0000
    $message->setTPDU('6000060000');
    /**
     * 设置 报文头(12位) => {应用类别(N2)}+软件版本号(N2)+终端状态(N1)+处理要求(N1)+保留使用(N6) 60 02 0 0 000000
     * 应用类别定义   磁条卡:60 IC卡: 61
     * 软件版本     01 国际,非全报文加密 02  国密,非全报文加密 80  全报文加密
     * 终端状态     0 正常交易状态 1 测试交易状态
     * 处理要求     0 无处理要求 1 下传终端参数 2 上传终端版本号及状态信息 3 重新签到 4 通知终端发起更新公钥信息操作 5 下载终端IC卡参数
     * 保留使用     000000
     */
    $message->setHeader('600210000000');
    /** 
     * 加密信息域 => 报文长度(A3)+算法标识(A1)+{商户号(A15)}+{终端号(A8)}+交易标识(A10)+响应码(A2)+保留(A2)    
     *     108 3 105431080620049 10009464 0200 000155 00 FF
     * 报文长度: 指应用数据明文长度,此处不设置,pack时生成
     * 算法标识: 0 明文数据(仅限于签到交易) 1 DES算法(不支持) 2 3DES 算法 3 国密算法
     * 交易标识:4个MTI(8583d的MsgType)+流水号;若交易报文存在11域,则流水号需要与8583的11域相等。
     * 响应码:2位ASCII;若系统解密OK,则填充“00”;否则填充“ER”表示系统解密失败;系统原包返回。
     * 保留:填充“FF”
     */
    $message->setEncrypted('010543108062004910009464020000015500FF');
    return $message;
  }

  /**
   * Get fully prepared ISO8583 Message.
   *
   * @return \ISO8583\Message
   */
  protected function getPreparedMessage()
  {
    $message = $this->getEmptyMessage();

    // $message->setField(4, "000000000000");
    // $message->setField(7, date('mdHis'));
    // $message->setField(11, substr($this->generateRequestId(), -6));
    // $message->setField(18, Settings::MERCHANT_TYPE);
    // $message->setField(19, Settings::CURRENCY_CODE);
    // $message->setField(22, Settings::POS_ENTRY_MODE_MAGNETIC);
    // $message->setField(25, Settings::POS_CONDITION_CODE_NORMAL);
    // $message->setField(32, Settings::ACQUIRING_INSTITUTION_ID);
    // $message->setField(37, substr(date('yz'), 1) . substr($this->generateRequestId(), -8));
    // $message->setField(41, Settings::TERMINAL_ID);
    // $message->setField(42, Settings::ACCEPTOR_ID);
    // $message->setField(49, Settings::CURRENCY_CODE);
    // $message->setField(60, Settings::ADDITIONAL_POS_INFO);

    return $message;
  }

  /**
   * Return random request ID
   *
   * @return integer
   */
  protected function generateRequestId()
  {
    return rand(10000000, 99999999);
  }

  public function check()
  {
		$message = $this->request()->pack();
		$answer = $this->sendMessage($message);
    $unpackedAnswer = $this->getEmptyMessage();
		$unpackedAnswer->unpack($answer);

		return $this->expectation($unpackedAnswer);
  }

	public function setConnection()
	{
		$config = [];
    if ($this->config->get('host')) {
      $config['host'] = $this->config->get('host');
    }

    if ($this->config->get('port')) {
      $config['port'] = $this->config->get('port');
    }

		$attempts   = 0;
		$lastError  = null;
		$errors     = [];
    
		while($attempts < 10) {
			try {
				$connection = new Connection($config);
	    	$connection->connect();
				break;
			} catch(\Exception $e) {
				sleep(1);
				$attempts++;
				$lastError = $e->getMessage();
				$errors[] = $e->getMessage();
			}
		}

		var_dump($errors);

		if ($attempts === 10) {
			throw new \Exception($lastError);
		}

		$this->connection = $connection;
	}

	public function resetConnection()
	{
		$this->connection->disconnect();
		$this->connection = null;
	}

	protected function sendMessage($message)
	{
    return $this->connection->write($message);
	}

  abstract protected function request();
  abstract protected function expectation($response);
}