<?php
/**
 * Created by PhpStorm.
 * User: zerox
 * Date: 25.08.18
 * Time: 19:27
 */

namespace VK;

require_once('config_library.php');


/**
 * Class Auth
 * @package VK
 */
class Auth
{
    /**
     * @var null
     */
    private $login = null;
    /**
     * @var null
     */
    private $pass = null;
    /**
     * @var mixed|null
     */
    private $cookie = null;
    /**
     * @var mixed|string
     */
    private $useragent = DEFAULT_USERAGENT;
    /**
     * @var mixed|string
     */
    private $id_app = DEFAULT_ID_APP;
    /**
     * @var string
     */
    private $access_token = '';
    /**
     * @var string
     */
    private $scope = '';
    /**
     * @var string
     */
    private $method = '';
    /**
     * @var string
     */
    private $default_scope = DEFAULT_SCOPE;
    /**
     * @var int
     */
    private $is_auth = 0;
    /**
     * @var int
     */
    private $auth_method = 0; //0 - mobile, 1 - app
    /**
     * @var null
     */
    private $captcha_sid = null;

    /**
     * Auth constructor.
     * @param $login
     * @param null $pass
     * @param null $other
     * @param bool $mobile
     * @throws VkApiException
     */
    public function __construct($login, $pass = null, $other = null, $mobile = true)
    {
        if (!isset($login))
            throw new VkApiException("Укажите логин и пароль либо куки");
        if (is_array($other)) {
            if (isset($other['useragent']))
                $this->useragent = $other['useragent'];
            if (isset($other['id_app']))
                $this->id_app = $other['id_app'];
        }
        if (isset($pass)) {
            $this->login = $login;
            $this->pass = $pass;
            $this->method = 'pass';
            if (!$mobile)
                $this->auth_method = 1;
        } else {
            $this->method = 'cookie';
            $this->cookie = json_decode($login, true);
            $this->auth_method = 1;
        }
    }

    /**
     * @throws VkApiException
     */
    public function auth()
    {
        if ($this->auth_method == 0)
            throw new VkApiException("Только для авторизации через приложение");
        $this->loginInVK();
    }

    /**
     * @throws VkApiException
     */
    private function loginInVK()
    {
        $query_main_page = $this->getCURL('https://vk.com/');
        preg_match('/name=\"ip_h\" value=\"(.*?)\"/s', $query_main_page['body'], $ip_h);
        preg_match('/name=\"lg_h\" value=\"(.*?)\"/s', $query_main_page['body'], $lg_h);

        $values_auth = [
            'act' => 'login',
            'role' => 'al_frame',
            '_origin' => 'https://vk.com',
            'utf8' => '1',
            'email' => $this->login,
            'pass' => $this->pass,
            'lg_h' => $lg_h[1],
            'ig_h' => $ip_h[1]
        ];
        $get_url_redirect_auch = $this->getCURL('https://login.vk.com/?act=login', $values_auth);

        if (!isset($get_url_redirect_auch['header']['location']))
            throw new VkApiException("Ошибка, ссылка редиректа не получена");

        $auth_page = $this->getCURL($get_url_redirect_auch['header']['location'][0]);

        if (!isset($auth_page['header']['set-cookie']))
            throw new VkApiException("Ошибка, куки пользователя не получены");

        $this->is_auth = 1;
    }

    /**
     * @param $url
     * @param null $post_values
     * @param bool $cookie
     * @return array
     */
    private function getCURL($url, $post_values = null, $cookie = true)
    {
        if ($curl = curl_init()) {
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($curl, CURLOPT_USERAGENT, $this->useragent);
            if (isset($post_values)) {
                curl_setopt($curl, CURLOPT_POST, 1);
                curl_setopt($curl, CURLOPT_POSTFIELDS, $post_values);
            }
//
//        curl_setopt($curl, CURLOPT_HTTPHEADER, [
//            "Content-Type: application/x-www-form-urlencoded",
//            "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
//            "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36"
//        ]);

            if ($cookie and isset($this->cookie)) {
                $send_cookie = [];
                foreach ($this->cookie as $cookie_name => $cookie_val) {
                    $send_cookie[] = "$cookie_name=$cookie_val";
                }
                curl_setopt($curl, CURLOPT_COOKIE, join('; ', $send_cookie));
            }

            curl_setopt($curl, CURLOPT_HEADERFUNCTION,
                function ($curl, $header) use (&$headers) {
                    $len = strlen($header);
                    $header = explode(':', $header, 2);
                    if (count($header) < 2) // ignore invalid headers
                        return $len;

                    $name = strtolower(trim($header[0]));
                    if (isset($headers) and !array_key_exists($name, $headers))
                        $headers[$name] = [trim($header[1])];
                    else
                        $headers[$name][] = trim($header[1]);

                    return $len;
                }
            );

            $out = curl_exec($curl);
            curl_close($curl);
            if (isset($headers['set-cookie']))
                $this->parseCookie($headers['set-cookie']);
            return ['header' => $headers, 'body' => $out];
        }
    }

    /**
     * @param $new_cookie
     */
    private function parseCookie($new_cookie)
    {
        foreach ($new_cookie as $cookie) {
            preg_match("!(.*?)=(.*?);(.*)!s", $cookie, $preger);
            if ($preger[2] == 'DELETED')
                unset($this->cookie[$preger[1]]);
            else
                $this->cookie[$preger[1]] = $preger[2] . ';' . $preger[3];
        }
    }

    /**
     * @return false|string
     */
    public function dumpCookie()
    {
        return json_encode($this->cookie);
    }

    /**
     * @return bool
     */
    public function isAuth()
    {
        $header = $this->getCURL("https://vk.com/feed")['header'];
        if (isset($header['location'][0]) and strpos($header['location'][0], 'login.vk.com'))
            return False;
        return True;
    }

    /**
     * @param null $captcha_key
     * @param null $captcha_sid
     * @return string
     * @throws VkApiException
     */
    public function getAccessToken($captcha_key = null, $captcha_sid = null)
    {
        if ($this->access_token != '')
            return $this->access_token;
        if ($this->auth_method) {
            if ($this->is_auth == 0)
                $this->loginInVK();
            if ($this->access_token == '')
                $this->access_token = $this->generateAccessToken();
        } else {
            if (isset($this->captcha_sid))
                $captcha_sid = $this->captcha_sid;
            $this->access_token = $this->generateAccessTokenMobile($captcha_key, $captcha_sid);
        }
        return $this->access_token;
    }

    /**
     * @param null $scope
     * @param bool $resend
     * @return mixed
     * @throws VkApiException
     */
    private function generateAccessToken($scope = null, $resend = false)
    {
        $this->scope = [];
        if (!isset($scope)) {
            $scope = $this->default_scope;
        }
        foreach (preg_split("!,!", $scope) as $one_scope)
            $this->scope[] = $one_scope;
        $scope = "&scope=$scope";

        if ($resend)
            $scope .= "&revoke=1";

        $token_url = 'https://oauth.vk.com/authorize?client_id=' . $this->id_app .
            $scope .
            '&response_type=token';

        $get_url_token = $this->getCURL($token_url);

        if (isset($get_url_token['header']['location'][0]))
            $url_token = $get_url_token['header']['location'][0];
        else {
            preg_match('!location.href = "(.*)"\+addr!s', $get_url_token['body'], $url_token);

            if (!isset($url_token[1])) {
                throw new VkApiException("Не получилось получить токен на этапе получения ссылки подтверждения");
            }
            $url_token = $url_token[1];
        }

        $access_token_location = $this->getCURL($url_token)['header']['location'][0];

        if (preg_match("!access_token=(.*?)&!s", $access_token_location, $access_token) != 1)
            throw new VkApiException("Не удалось найти access_token в строке ридеректа, ошибка:" . $this->getCURL($access_token_location, null, false)['body']);
        return $access_token[1];
    }

    /**
     * @param $captcha_key
     * @param $captcha_sid
     * @return mixed
     * @throws VkApiException
     */
    private function generateAccessTokenMobile($captcha_key, $captcha_sid)
    {
        if (!isset($this->pass))
            throw new VkApiException("Метод работает только с логином и паролем");

        $captcha = '';
        $this->scope = [];
        $scope = $this->default_scope;
        foreach (preg_split("!,!", $scope) as $one_scope)
            $this->scope[] = $one_scope;
        $scope = "&scope=$scope";

        if (isset($captcha_sid) and isset($captcha_key))
            $captcha = "&captcha_sid=$captcha_sid&captcha_key=$captcha_key";

        $token_url = 'https://oauth.vk.com/token?grant_type=password&client_id=2274003&client_secret=hHbZxrka2uZ6jB1inYsH' .
            '&username=' . $this->login .
            '&password=' . $this->pass .
            $scope .
            $captcha;
        $response_auth = $this->getCURL($token_url, null, false)['body'];
        $response_auth = json_decode($response_auth, true);

        if (isset($response_auth['access_token']))
            return $response_auth['access_token'];
        else
            throw new VkApiException(json_encode($response_auth));
    }
}