<?php

namespace App\Services\Integrations\Tickets\Jira;

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
use App\Models\ApiCredential;
use App\Models\FailedTicketHistory;
use Illuminate\Support\Facades\Auth;
use File;
use Illuminate\Support\Facades\Log;

class OAuthWrapper
{

    /**
     * @var string
     */
    protected $requestTokenUrl = 'plugins/servlet/oauth/request-token?oauth_callback=%s';
    /**
     * @var string
     */
    protected $requestAuthUrl = 'plugins/servlet/oauth/authorize?oauth_token=%s';
    /**
     * @var string
     */
    protected $requestAccessTokenUrl = 'plugins/servlet/oauth/access-token?oauth_verifier=%s';
    /**
     * @var string
     */
    protected $baseUrl;
    /**
     * @var string
     */
    protected $consumerKey;
    /**
     * @var string
     */
    protected $consumerSecret;
    /**
     * @var string
     */
    protected $privateKeyFile;
    /**
     * @var string
     */
    protected $privateKeyPassword;
    /**
     * JiraOAuthClient constructor.
     *
     * @param string $baseUrl
     * @param string $consumerKey
     * @param string $consumerSecret
     * @param string $privateKeyFile
     * @param string $privateKeyPassword
     */
    public function __construct()
    {
        $this->consumerSecret = config('jira.consumer_secret');
        $this->privateKeyFile = config('jira.private_key');
        $this->privateKeyPassword = null;
        $this->isHeaderSet = config('jira.is_header_set');
        $this->bearerToken = config('jira.bearer_token');

        $this->keyword = config('jira.keyword');
        $this->issueType = config('jira.issueType');
    }

    /**
     * Gets the Jira API credentials
     * @return object
     */
    public function getCredentials()
    {
        $jiraCredential = ApiCredential::where('slug', 'jira')->first();
        $credentials = [
            'baseUrl' => optional($jiraCredential)->url,
            'consumerKey' => optional($jiraCredential)->password,
            'jiraCredential' => $jiraCredential
        ];

        return (object) $credentials;
    }
    /**
     * @param string $token
     * @param string $tokenSecret
     *
     * @return Client
     */
    public function getClient($token = null, $tokenSecret = null)
    {
        $credentials = $this->getCredentials();
        $stack = HandlerStack::create();
        $middleware = new Oauth1([
            'consumer_key' => $credentials->consumerKey,
            'consumer_secret' => $this->consumerSecret,
            'token' => $token,
            'token_secret' => $tokenSecret,
            'private_key_file' => $this->privateKeyFile,
            'private_key_passphrase' => $this->privateKeyPassword,
            'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
        ]);
        $stack->push($middleware);

        $this->client = new Client([
            'base_uri' => $credentials->baseUrl,
            'auth' => 'oauth',
            'handler' => $stack
        ]);

        if ($this->isHeaderSet == 1) {
            $this->client = new Client([
                'base_uri' => $credentials->baseUrl,
                'handler' => $stack,
                'headers' => [
                    'Accept' => 'application/json',
                    'Authorization' => 'Bearer ' . $this->bearerToken,
                    'Content-Type' => 'application/json',
                ],
            ]);
        }
        return $this->client;
    }
    /**
     * @param string $callbackUrl
     *
     * @return string
     */
    public function getRequestTokenUrl($callbackUrl)
    {
        $credentials = $this->getCredentials();
        return $credentials->baseUrl . sprintf($this->requestTokenUrl, $callbackUrl);
    }
    /**
     * @param string $token
     *
     * @return string
     */
    public function getAuthorizeUrl($token)
    {
        $credentials = $this->getCredentials();
        return $credentials->baseUrl . sprintf($this->requestAuthUrl, $token);
    }
    /**
     * @param string $verifier
     *
     * @return string
     */
    public function getRequestAccessTokenUrl($verifier)
    {
        $credentials = $this->getCredentials();
        return $credentials->baseUrl . sprintf($this->requestAccessTokenUrl, $verifier);
    }
    /**
     * @param string $callbackUrl
     *
     * @return array Temporary tokens
     */
    public function login($callbackUrl)
    {
        $guzzle = $this->getClient();
        $response = $guzzle->post($this->getRequestTokenUrl($callbackUrl))
            ->getBody()->getContents();
        parse_str($response, $tokens);
        return $tokens;
    }
    /**
     * @param array $tempTokens
     * @param string $verifier
     *
     * @return array Access tokens
     */
    public function requestAccessToken(array $tempTokens, $verifier)
    {
        $guzzle = $this->getClient($tempTokens['oauth_token'], $tempTokens['oauth_token_secret']);
        $response = $guzzle->post($this->getRequestAccessTokenUrl($verifier), [
            'oauth_token' => $tempTokens['oauth_token'],
            'oauth_token_secret' => $tempTokens['oauth_token_secret'],
        ])->getBody()->getContents();
        parse_str($response, $tokens);
        return $tokens;
    }

    public function getAuthorizedUser($tokens)
    {
        if (session()->has('jiraConnectionValid')) {
            return session('jiraConnectionValid');
        }
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        $user = $guzzle->get('rest/api/2/myself')->getBody()->getContents();
        session(['jiraConnectionValid' => json_decode($user, true)]);
        return session('jiraConnectionValid');
    }

    public function getIssueId($tokens, $ticketId)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        $response = $guzzle->get('rest/api/2/issue/' . $ticketId)->getBody()->getContents();
        return json_decode($response, true);
    }

    public function addComment($tokens, $ticketId, $description, $userId)
    {
        $description = preg_replace(array('/"/', '/\\\/'), '', str_replace('\n', '*newline*', $description));
        $description = str_replace('*newline*', '\n', $description);
        $data   = [
            'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'],
            'body'    => '{"body" : "' . $description . '"}'
        ];
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        try {
            $response = $guzzle->post('rest/api/2/issue/' . $ticketId . '/comment', $data)->getBody()->getContents();
            return json_decode($response, true);
        } catch (\Exception $e) {
            Log::channel('daily')->info("Jira Ticket:$ticketId, Description:$description, User:$userId, Exception:" . $e->getMessage());
            FailedTicketHistory::create([
                'user_id' => $userId,
                'ticket_id' => $ticketId,
                'description' => str_replace("\\n", '', $description),
                'exceptions' => json_encode($e->getMessage())
            ]);
            return false;
        }
    }


    /**
     * create jira ticket
     * @param mixed $tokens
     * @param mixed $data
     * 
     * @return Array
     */
    public function createTicket($tokens, $data)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        try {
            $response = $guzzle->post('rest/api/2/issue', [
                \GuzzleHttp\RequestOptions::JSON => [
                    "fields" => [
                        "project" => ["key" => $this->keyword],
                        "summary" => $data['summary'],
                        "description" => $data['description'],
                        "issuetype" => ["name" => $this->issueType]
                    ]
                ]
            ])->getBody()->getContents();
            $response = json_decode($response, true);
            Log::channel('daily')->info('New Jira ticket Created :  ' . $response['key']);
            return $response;
        } catch (\Exception $e) {
            // dd($e->getMessage());
            Log::channel('daily')->info('Jira Ticket Create Error :  ' . $e->getMessage());

            return false;
        }
    }


    /**
     * Update Jira issue assignee field
     * Reference URL : https://bit.ly/2LsD4JT.
     *
     * @param array  $tokens   API connection authentication data
     * @param string $ticketId Jira ticket id
     * @param string $assignee Assignee Email
     *
     * @return bool
     */
    public function updateIssueAssignee($tokens, $ticketId, $assignee)
    {
        $jiraUserData = $this->searchJiraUser($tokens, $assignee);

        if ($jiraUserData !== false && is_array($jiraUserData) && isset($jiraUserData[0]['accountId'])) {
            $apiData = [
                'headers' => ['Content-Type' => 'application/json', 'Accept' => 'application/json'],
                'body' => '{"accountId":"' . $jiraUserData[0]['accountId'] . '"}',
            ];
            $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
            try {
                $response = $guzzle->put('rest/api/2/issue/' . $ticketId . '/assignee', $apiData)->getBody()->getContents();

                return true;
            } catch (\Exception $e) {
                return false;
            }
        }

        return false;
    }


    /**
     * Search user by email
     * Reference URL: https://bit.ly/2YUozlm
     * Reference URL: https://bit.ly/2YYMjEH.
     *
     * @param array  $tokens API connection authentication data
     * @param string $email  Search user email
     *
     * @return array
     */
    public function searchJiraUser($tokens, $email)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        try {
            $user = $guzzle->get('rest/api/2/user/search?query=' . $email)->getBody()->getContents();
            return json_decode($user, true);
        } catch (\Exception $e) {
            return false;
        }
    }


    /**
     * Calling API to get Jira issue details by using user email.
     *
     * @param array  $tokens           API connection authentication data
     * @param string $email      User Email
     *
     * @return array
     */
    public function getIssueByUserEmailAndComponent($tokens, $email)
    {
        $fields = implode(',', config('jira-fields.display'));

        $componentCond   = "'component' = '" . config('jira-fields.search.component') . "' ";
        $emailField = config('jira-fields.search.email');

        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        $response = $guzzle->get("rest/api/2/search?jql=$componentCond AND $emailField ~ '" . $email . "'&fields=id,key," . $fields . "&maxResults=1")->getBody()->getContents();

        return json_decode($response, true);
    }


    /**
     * Get an issue current status.
     *
     * @param array  $tokens   API connection authentication data
     * @param string $ticketId Jira issue id
     *
     * @return array
     */
    public function getIssueCurrentStatus($tokens, $ticketId)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        try {
            $response = $guzzle->get('rest/api/2/issue/' . $ticketId . '?fields=status')->getBody()->getContents(); //issue current status

            return json_decode($response, true);
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Calling JIRA API to get all custom fields used.
     *
     * @param array $tokens API connection authentication data
     *
     * @return array
     */
    public function allCustomFields($tokens)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        $response = $guzzle->get('rest/api/latest/field/')->getBody()->getContents();

        return json_decode($response, true);
    }

    /**
     * Calling JIRA API to post attachment as comment
     *
     * @param array $tokens API connection authentication data
     * @param string $ticketId Jira issue id
     * @param string $file File to attach
     *
     * @return array
     */
    public function addAttachmentAsComment($tokens, $ticketId, $userId, $file, $fileName)
    {
        $guzzle = $this->getClient($tokens['oauth_token'], $tokens['oauth_token_secret']);
        try {
            $response = $guzzle->post('rest/api/2/issue/' . $ticketId . '/attachments', [
                'headers' => ['X-Atlassian-Token' => 'no-check'],
                'multipart' => [
                    [
                        'name'     => 'file',
                        'contents' => fopen($file, 'r'),
                        'filename' => $fileName
                    ],
                ]
            ]);
            $response->withHeader('Content-Type', 'multipart/form-data');
            $response = $response->getBody()->getContents();
            $response = json_decode($response, true);
            return !empty($response[0]['id']);
        } catch (\Exception $e) {
            // dd($e->getMessage());
            FailedTicketHistory::create([
                'user_id'     => $userId,
                'ticket_id'   => $ticketId,
                'description' => 'Action history attachment upload on file ' . $file,
                'exceptions'  => json_encode($e->getMessage())
            ]);
            return false;
        }
    }
}
