<?php namespace Collect\Lib;
use Dever;
class Api
{
    protected $api;
    protected $service;
    protected $domain;
    protected $param = array();

    # 执行某个业务接口
    public function run($api_id, $api_service_id, $param = array())
    {
        $this->api = Dever::db('api', 'collect')->find($api_id);
        if (!$this->api) {
            return false;
        }
        if (isset($param['host'])) {
            $this->api['host'] = $param['host'];
        }
        if (isset($param['appkey'])) {
            $this->api['appkey'] = $param['appkey'];
        }
        if (isset($param['appsecret'])) {
            $this->api['appsecret'] = $param['appsecret'];
        }
        $this->service = Dever::db('api_service', 'collect')->find(array('api_id' => $this->api['id'], 'id' => $api_service_id));
        if (!$this->service) {
            return false;
        }
        $this->param = $param;
        return $this->curl();
    }

    # 发起请求
    protected function curl($url = false)
    {
        if (!$url) {
            $url = $this->url();
        }
        $header = $body = array();
        $method = 'get';
        $json = false;
        if ($this->service['method'] == -1) {
            $this->service['method'] = $this->api['method'];
            $this->service['post_method'] = $this->api['post_method'];
        }
        if ($this->service['post_method'] == 2) {
            $method = 'file';
        } elseif ($this->service['method'] == 2) {
            $method = 'post';
        }
        if ($this->service['post_method'] == 3) {
            $json = true;
        }

        $this->request($header, 'request_header', array('api_id' => $this->api['id']));
        $this->request($body, 'request_body', array('api_id' => $this->api['id']));
        $this->request($header, 'api_service_request_header', array('api_service_id' => $this->service['id']));
        $this->request($body, 'api_service_request_body', array('api_service_id' => $this->service['id']));

        $this->sign($body);
        $response = Dever::curl($url, $body, $method, $json, $header)->result();
        $test = Dever::input('test');
        if ($test && $test == 1) {
            $result = array
            (
                'url' => $url,
                'header' => $header,
                'method' => $method,
                'json' => $json,
                'body' => $body,
                'response' => $response,
            );
            echo Dever::json_encode($result);die;
        }
        $response = $this->response($response);
        $response['request'] = $body;
        return $response;
    }

    # 处理url
    protected function url()
    {
        if (strstr($this->service['uri'], 'http')) {
            $this->api['host'] = '';
        }
        $uri = Dever::db('api_service_uri', 'collect')->select(array('api_service_id' => $this->service['id']));
        if ($uri) {
            $path = array();
            $param = array();
            foreach ($uri as $k => $v) {
                $v['value'] = $this->value(array(), $v['key'], $v['value']);
                if ($v['type'] == 1) {
                    $path[] = $v['value'];
                } elseif ($v['type'] == 2) {
                    $path[] = $v['key'] . '/' . $v['value'];
                } elseif ($v['type'] == 3) {
                    $path[] = $v['key'] . '=' . $v['value'];
                } elseif ($v['type'] == 4) {
                    $param[] = $v['value'];
                } elseif ($v['type'] == 5) {
                    $param[] = $v['key'] . '=' . $v['value'];
                }
            }
            if ($path) {
                $this->service['uri'] .= implode('/', $path);
            }
            if ($param) {
                if (!strstr($this->service['uri'], '?')) {
                    $this->service['uri'] .= '?';
                }
                $this->service['uri'] .= implode('&', $param);
            }
        }
        return $this->domain = $this->api['host'] . $this->service['uri'];
    }

    # 获取请求参数
    protected function request(&$data, $table, $where)
    {
        $body = Dever::db($table, 'collect')->select($where);
        if ($body) {
            foreach ($body as $k => $v) {
                $value = $this->value($data, $v['key'], $v['default'], $v['type']);
                if ($value) {
                    $data[$v['key']] = $value;
                }
            }
        }
    }

    # 数据响应格式处理
    protected function response($response)
    {
        if ($this->api['response_type'] == 1) {
            $response = $this->filter($response);
            return array
            (
                'status' => 1,
                'msg' => 'ok',
                'response' => false,
                'request' => array(),
                'data' => $response,
            );
        }
        if ($this->api['response_type'] == 2) {
            $response = Dever::json_decode($response);
        } elseif ($this->api['response_type'] == 3) {
            $response = (array) simplexml_load_string($response);
        } else {
            if (strstr($response, ',')) {
                $response = explode(',', $response);
            } elseif (strstr($response, ' ')) {
                $response = explode(' ', $response);
            } elseif (strstr($response, '|')) {
                $response = explode('|', $response);
            } else {
                $response = explode("\n", $response);
            }
        }
        $msg = '';
        $status = 2;
        if (isset($response[$this->api['response_code']])) {
            $code = $response[$this->api['response_code']];
            $code = Dever::db('response_code', 'collect')->find(array('api_id' => $this->api['id'], 'value' => $code));
            if ($code && $code['type'] == 1) {
                $status = 1;
            }
        }
        $msg = $response[$this->api['response_msg']] ?? 'no';
        $data = '';
        if (strstr($this->api['response_data'], ',')) {
            $temp = explode(',', $this->api['response_data']);
            foreach ($temp as $k => $v) {
                if (isset($response[$v])) {
                    $data = $response[$v];
                    break;
                }
            }
        } elseif (strstr($this->api['response_data'], '.')) {
            $temp = explode('.', $this->api['response_data']);
            $data = isset($response[$temp[0]][$temp[1]]) ? $response[$temp[0]][$temp[1]] : (isset($response[$temp[0]]) ? $response[$temp[0]] : false);
        } else {
            $data = $response[$this->api['response_data']] ?? $response;
        }
        if ($data) {
            $api_service_response = Dever::db('api_service_response', 'collect')->select(array('api_service_id' => $this->service['id']));
            if ($api_service_response) {
                foreach ($api_service_response as $k => $v) {
                    if (isset($data[$v['key']])) {
                        $data[$v['key']] = $this->value($data, $v['key'], $v['key'], $v['type'] * 100);
                    }
                }
            }
            $data = $this->handle($data);
        }
        return array
        (
            'status' => $status,
            'msg' => $msg,
            'response' => $response,
            'request' => array(),
            'data' => $data,
        );
    }

    protected function sign(&$body)
    {
        if ($this->api['sign_method'] == 1) {
            return;
        }
        if (!$this->api['sign_name']) {
            $this->api['sign_name'] = 'signature';
        }
        $sign = array();
        if ($this->service['sign_col']) {
            $col = explode('+', $this->service['sign_col']);
            foreach ($col as $k => $v) {
                $sign[$v] = $this->value($body, $v, $v, 1);
            }
        } elseif ($this->api['sign_col']) {
            $col = explode('+', $this->api['sign_col']);
            foreach ($col as $k => $v) {
                $sign[$v] = $this->value($body, $v, $v, 1);
            }
        } else {
            $sign = $body;
        }

        if ($this->api['sign_appsecret'] && $this->api['sign_appsecret_location'] == 1) {
            $sign[$this->api['sign_appsecret']] = $this->api['appsecret'];
            if (isset($sign['api_service_product'])) {
                $sign[$this->api['sign_appsecret']] = sha1($this->api['appkey'] . '|' . $this->api['appsecret']);
            }
        }

        if ($this->api['sign_sort'] == 2) {
            ksort($sign);
        }

        $string = '';
        foreach ($sign as $k => $v) {
            if ($this->api['sign_empty'] == 2 && !$v) {
                continue;
            }
            if ($this->api['sign_encode'] == 2 && strstr($v, 'http')) {
                $v = urlencode($v);
                if (isset($body[$k])) {
                    $body[$k] = $v;
                }
            }
            if ($this->api['sign_type'] == 1) {
                $string .= $v;
            } elseif ($this->api['sign_type'] == 2) {
                $string .= $k . '=' . $v . '&';
            } elseif ($this->api['sign_type'] == 3) {
                $string .= $k . $v;
            } elseif ($this->api['sign_type'] == 4) {
                $string .= $k . $v . '&';
            } elseif ($this->api['sign_type'] == 5) {
                $string .= $k . '=' . $v;
            }
        }

        $sign = rtrim($string, '&');

        if ($this->api['sign_appsecret'] && $this->api['sign_appsecret_location'] == 2) {
            if ($this->api['sign_type'] == 1) {
                $sign .= $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 2) {
                $sign .= '&' . $this->api['sign_appsecret'] . '=' . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 3) {
                $sign .= $this->api['sign_appsecret'] . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 4) {
                $sign .= '&' . $this->api['sign_appsecret'] . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 5) {
                $sign .= $this->api['sign_appsecret'] . $this->api['appsecret'];
            }
        } elseif ($this->api['sign_appsecret'] && $this->api['sign_appsecret_location'] == 3) {
            if ($this->api['sign_type'] == 1) {
                $sign = $this->api['appsecret'] . $sign;
            } elseif ($this->api['sign_type'] == 2) {
                $sign = $this->api['sign_appsecret'] . '=' . $this->api['appsecret'] . '&' . $sign;
            } elseif ($this->api['sign_type'] == 3) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . $sign;
            } elseif ($this->api['sign_type'] == 4) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . '&' . $sign;
            } elseif ($this->api['sign_type'] == 5) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . $sign;
            }
        } elseif ($this->api['sign_appsecret'] && $this->api['sign_appsecret_location'] == 4) {
            if ($this->api['sign_type'] == 1) {
                $sign = $this->api['appsecret'] . $sign . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 2) {
                $sign = $this->api['sign_appsecret'] . '=' . $this->api['appsecret'] . '&' . $sign . '&' . $this->api['sign_appsecret'] . '=' . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 3) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . $sign . $this->api['sign_appsecret'] . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 4) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . '&' . $sign . '&' . $this->api['sign_appsecret'] . $this->api['appsecret'];
            } elseif ($this->api['sign_type'] == 5) {
                $sign = $this->api['sign_appsecret'] . $this->api['appsecret'] . $sign . $this->api['sign_appsecret'] . $this->api['appsecret'];
            }
        }

        # 如果是ssl 这里需要处理一下，后续处理吧

        if ($this->api['sign_method'] == 2) {
            $sign = md5($sign);
        } elseif ($this->api['sign_method'] == 3) {
            $sign = hash("sha256", $sign);
        } elseif ($this->api['sign_method'] == 4) {
            $sign = sha1($sign);
        }

        if ($this->api['sign_after'] == 2) {
            $sign = strtoupper($sign);
        } elseif ($this->api['sign_after'] == 2) {
            $sign = strtolower($sign);
        }

        $body[$this->api['sign_name']] = $sign;


        return $sign;
    }

    protected function value($data, $key, $value, $type = 1)
    {
        $value = trim($value);

        if ($this->param && isset($this->param[$value])) {
            $value = $this->param[$value];
        } elseif ($data && isset($data[$value])) {
            $value = $data[$value];
        } elseif (isset($this->api[$value]) && $this->api[$value]) {
            $value = $this->api[$value];
        } elseif ($value == 'notify') {
            $value = $this->notify($this->param['order_id'] ?? '');
        } elseif ($value == 'api_request') {
            $value = $data;
        } elseif ($value == 'timestamp') {
            $value = \Dever\Helper\Secure::timestamp();
        } elseif ($value == 'nonce') {
            $value = \Dever\Helper\Secure::nonce();
        } elseif ($value == 'token') {
            $value = $this->token(1, 'token');
        } elseif ($value == 'ticket') {
            $value = $this->token(2, 'ticket');
        } elseif ($a = strstr($value, '{') || strstr($value, '(')) {
            if ($a) {
                $func = function ($r) use($data) {
                    return $this->value($data, $r[1], $r[1]);
                };
                $value = preg_replace_callback('/{(.*?)}/', $func, $value);
            }
            $value = '$value = '.$value.';';
            eval($value);
        }

        if (!$value) {
            $key = trim($key);
            return Dever::input($key);
        }

        if ($type == 1 || $type == 100) {
            $value = (string) $value;
        } elseif ($type == 2 || $type == 200) {
            $value = (float) $value;
        } elseif ($type == 3 && is_array($value)) {
            $value = Dever::json_encode($value);
        } elseif ($type == 4) {
            $value = $this->encrypt($value);
        } elseif ($type == 300 && is_array($value)) {
            $value = Dever::json_decode($value);
        } elseif ($type == 400) {
            $value = $this->decrypt($value);
        }

        return $value;
    }

    protected function handle($data)
    {
        # 转换处理
        $convert = Dever::db('api_convert', 'collect')->select(array('api_id' => $this->api['id']));

        $param = array();
        if ($convert && is_array($data)) {
            if (isset($data[0])) {
                foreach ($data as $k1 => $v1) {
                    foreach ($convert as $k => $v) {
                        if (isset($data[$k1][$v['before']])) {
                            $param[$k1][$v['after']] = $data[$k1][$v['before']];
                        }
                    }
                }
            } else {
                foreach ($convert as $k => $v) {
                    if (isset($data[$v['before']])) {
                        $param[$v['after']] = $data[$v['before']];
                    }
                }
            }
        } else {
            $param = $data;
        }
        return $param;
    }

    # 生成回调
    protected function notify($order_id)
    {
        $encode = \Dever\Helper\Str::encode($this->api['id'] . '|' . $this->service['id'] . '|' . $order_id);
        return Dever::url('collect/notify.common', array('s' => $encode));
    }

    protected function token($id = 1, $type = 'token')
    {
        return '';
        $db = Dever::db($type, 'collect');
        $data['api_id'] = $collect['id'];
        $data['api_service_id'] = $api['id'];
        $info = $db->find($data);
        if ($info && time() - $info['mdate'] >= $info['expires']) {
            
        } elseif ($info) {
            return $info['value'];
        }
        $result = $this->init()->curl();
        if (isset($result[$type])) {
            $data['value'] = $result[$type];
            $data['expires'] = '7200';
            if (isset($result['expires'])) {
                $data['expires'] = $result['expires'];
            }
            if ($info) {
                $data['where_id'] = $info['id'];
                $db->update($data);
            } else {
                $db->insert($data);
            }
            return $result[$type];
        }
        return '';
    }

    protected function encrypt($value)
    {
        if ($this->api['ssl_method'] && $this->api['ssl_key']) {
            $value = openssl_encrypt($value, $this->api['ssl_method'], $this->api['ssl_key'], $this->api['ssl_option'], $this->api['ssl_iv']);
            if ($this->api['ssl_after'] == 2) {
                $value = base64_encode($value);
            }
        }
        
        return $value;
    }

    protected function decrypt($value)
    {
        if ($this->api['ssl_method'] && $this->api['ssl_key']) {
            if (is_array($value)) {
                $value = Dever::json_encode($value);
            }
            $value = openssl_decrypt($value, $this->api['ssl_method'], $this->api['ssl_key'], $this->api['ssl_option'], $this->api['ssl_iv']);
            if ($this->api['ssl_after'] == 2) {
                $value = base64_decode($value);
            }
        }
        
        return $value;
    }

    protected function public_encrypt()
    {
        $publicKey = "-----BEGIN PUBLIC KEY-----\n" .
            chunk_split($this->nowpayPublickey, 64, "\n") .
            "-----END PUBLIC KEY-----\n";
        $output = '';
        openssl_public_encrypt($this->encryptKey, $output, $publicKey);
        return base64_encode($output);
    }
    
    protected function protected_decrypt($str)
    {
        $priKey = "-----BEGIN RSA protected KEY-----\n" .
            chunk_split($this->protectedKey, 64, "\n") .
            "-----END RSA protected KEY-----\n";
        $output = '';
        openssl_protected_decrypt(base64_decode($str), $output, $priKey);
        return $output;
    }

    protected function filter($data)
    {
        return preg_replace("/<!--[^\!\[]*?(?<!\/\/)-->/","",$data);
    }
}