<?php

namespace App\Services;

use App\Helper\Cache\UserJiraCacheHelper;
use App\Models\User;
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;

class JiraService
{
    protected $client;
    protected $baseUrl;
    protected $authHeader;

    public function __construct()
    {
        $this->baseUrl = env('JIRA_BASE_URL');
        $this->authHeader = 'Basic ' . base64_encode(env('JIRA_USERNAME') . ':' . env('JIRA_API_TOKEN'));
        $this->client = new Client([
            'base_uri' => $this->baseUrl,
            'headers' => [
                'Authorization' => $this->authHeader,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ]);
    }

    public function getAllProjects()
    {
        $response = $this->client->get('/rest/api/3/project');
        return json_decode($response->getBody()->getContents(), true);
    }

    public function getIssuesAsync($projectKey, $startAt = 0, $maxResults = 50)
    {
        $body = [
            'expand' => ['names', 'schema', 'operations'],
            'fields' => ['summary', 'status', 'description', 'timeoriginalestimate', 'timespent', 'worklog', 'assignee'],
            'jql' => "project = '{$projectKey}' ORDER BY created DESC",
            'maxResults' => $maxResults,
            'startAt' => $startAt
        ];

        return $this->client->postAsync($this->baseUrl . '/rest/api/3/search', [
            'body' => json_encode($body),
            'headers' => [
                'Authorization' => $this->authHeader,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ]);
    }
    public function getIssues($projectKey, $startAt = 0)
    {
        $body = [
            'expand' => ['names', 'schema', 'operations'],
            'fields' => ['summary', 'status', 'description', 'timeoriginalestimate', 'timespent', 'worklog', 'assignee'],
            'jql' => "project = '{$projectKey}' ORDER BY created DESC",
            'maxResults' => 100,
            'startAt' => $startAt
        ];

        $response = $this->client->post('/rest/api/3/search', [
            'body' => json_encode($body)
        ]);

        return json_decode($response->getBody()->getContents(), true);
    }

    public function getIssuesByUser($accountId, $startAt = 0)
    {
        $body = [
            'expand' => ['names', 'schema', 'operations'],
            'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project'],
            'jql' => sprintf(
                "assignee = '%s' AND status IN ('backlog', 'to do', 'in progress')",
                $accountId
            ),
            'maxResults' => 50,
            'startAt' => $startAt
        ];

        $response = $this->client->post('/rest/api/3/search', [
            'body' => json_encode($body)
        ]);

        $issues = json_decode($response->getBody()->getContents(), true);

        foreach ($issues['issues'] as &$issue) {
            $issueKey = $issue['key'];
            $issueResponse = $this->client->get("/rest/api/3/issue/{$issueKey}?expand=changelog");
            $issueDetails = json_decode($issueResponse->getBody()->getContents(), true);
            $issue['changelog'] = $issueDetails['changelog'];
        }

        return $issues;
    }

    public function getAllUsers()
    {
        $response = $this->client->get('/rest/api/3/users/search', [
            'headers' => [
                'Authorization' => $this->authHeader,
                'Accept' => 'application/json'
            ]
        ]);

        return json_decode($response->getBody()->getContents(), true);
    }

    public function getUserByEmail($email)
    {

        $response = $this->client->get("/rest/api/3/user/search?query={$email}", [
            'headers' => [
                'Authorization' => $this->authHeader,
                'Accept' => 'application/json'
            ]
        ]);

        if ($response->getStatusCode() == 200) {
            return json_decode($response->getBody(), true);
        }

        return null;
    }

    public function getUsersByEmails($all_users)
    {
        $users = [];

        foreach ($all_users as $user) {
            $user_info = $this->getUserByEmail($user->email);

            if (isset($user_info[0]['self'])) {
                $user_info[0]['emailAddress'] = $user->email;
                $users[] = $user_info;
            }
        }

        return $users;
    }

    public function getUserWorkLogs($accountId, $startDate, $endDate)
    {
        $body = [
            'jql' => "worklogAuthor = '{$accountId}'AND worklogDate >= '{$startDate}' AND worklogDate <= '{$endDate}'",
            'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'project'],
            'maxResults' => 100
        ];

        $response = $this->client->post('/rest/api/3/search', [
            'body' => json_encode($body),
            'headers' => [
                'Authorization' => $this->authHeader,
                'Accept' => 'application/json',
                'Content-Type' => 'application/json'
            ]
        ]);

        $data_response = json_decode($response->getBody()->getContents(), true);

        if ($data_response['total'] == 0) {
            return $data_response;
        }

        $promises = [];
        foreach ($data_response['issues'] as $index => $issue) {
            $issueId = $issue['id'];

            // Get the initial worklog data to determine total number of worklogs
            $promises[$index] = $this->client->getAsync("/rest/api/3/issue/{$issueId}/worklog", [
                'query' => [
                    'startAt' => 0,
                    'maxResults' => 1
                ]
            ])->then(function ($checkApiResponse) use ($issueId, $index) {
                $checkApi = json_decode($checkApiResponse->getBody()->getContents(), true);
                $maxResults = 100;
                $totalWorklogs = $checkApi['total'];
                return $this->client->getAsync("/rest/api/3/issue/{$issueId}/worklog", [
                    'query' => [
                        'startAt' => $totalWorklogs - $maxResults,
                        'maxResults' => $totalWorklogs
                    ]
                ])->then(function ($worklogResponse) use ($index) {
                    return [
                        'index' => $index,
                        'worklogs' => json_decode($worklogResponse->getBody()->getContents(), true)
                    ];
                });
            });
        }

        // Wait for all promises to complete
        $results = Utils::settle($promises)->wait();

        // Attach worklogs to issues
        foreach ($results as $result) {
            if ($result['state'] === 'fulfilled') {
                $data_response['issues'][$result['value']['index']]["fields"]['worklog'] = $result['value']['worklogs'];
            }
        }

        return $data_response;
    }

    public function getAllUserWorkLogs($startDate, $endDate)
    {
        $users = $this->getAllUsers();
        $workLogs = [];

        foreach ($users as $user) {
            $userWorkLogs = $this->getUserWorkLogs($user['accountId'], $startDate, $endDate);
            $assignTask = [];
            if (count($userWorkLogs['issues']) > 0) {
                $assignTask = $this->getIssuesByUser($user['accountId']);
                $workLogs[] = ['username' => $user['displayName'], 'information' => $userWorkLogs, 'tasksAssign' => $assignTask, 'user' => $user];
            }
        }

        return $workLogs;
    }

    public function getAllUserDoing()
    {
        $users = UserJiraCacheHelper::getCacheSetting();
        $groupedIssues = [];
        $users_data = [];
        $user_warning = [];
        foreach ($users as $user) {
            $user = (array)$user[0];
            $users_data[$user['displayName']]['user'] = $user;
            $users_data[$user['displayName']]['total_spent'] = 0;
            $users_data[$user['displayName']]['total_est'] = 0;
            $body = [
                'expand' => ['names', 'schema'],
                'fields' => ['summary', 'status', 'timeoriginalestimate', 'timespent', 'assignee', 'project', 'updated'],
                'jql' => sprintf(
                    "assignee = '%s' AND status IN ('to do', 'todo', 'in progress') ORDER BY updated DESC",
                    $user['accountId']
                ),
                'maxResults' => 50,
                'startAt' => 0
            ];

            $response = $this->client->post('/rest/api/3/search', [
                'body' => json_encode($body)
            ]);

            $issues = json_decode($response->getBody()->getContents(), true);

            if (count($issues['issues']) == 0) {
                $user_warning[] = $user;
            }

            foreach ($issues['issues'] as $issue) {
                $projectName = $issue['fields']['project']['name'];
                $username = $issue['fields']['assignee']['displayName'];

                if (!isset($groupedIssues[$projectName])) {
                    $groupedIssues[$projectName] = [];
                    $groupedIssues[$projectName]['project'] = $issue['fields']['project'];
                }

                if (!isset($groupedIssues[$projectName]['users'][$username])) {
                    $groupedIssues[$projectName]['users'][$username] = [];
                    $groupedIssues[$projectName]['users'][$username]['user'] = $issue['fields']['assignee'];
                    $groupedIssues[$projectName]['users'][$username]['p_total_spent'] = 0;
                    $groupedIssues[$projectName]['users'][$username]['p_total_est'] = 0;
                }

                $groupedIssues[$projectName]['users'][$username]['issues'][] = $issue;
                $groupedIssues[$projectName]['users'][$username]['p_total_spent'] = $groupedIssues[$projectName]['users'][$username]['p_total_spent'] + $issue['fields']['timespent'];
                $groupedIssues[$projectName]['users'][$username]['p_total_est'] = $groupedIssues[$projectName]['users'][$username]['p_total_est'] + ($issue['fields']['timeoriginalestimate'] ?? 0);

                $users_data[$user['displayName']]['total_spent'] = $users_data[$user['displayName']]['total_spent'] + $issue['fields']['timespent'];
                $users_data[$user['displayName']]['total_est'] = $users_data[$user['displayName']]['total_est'] + ($issue['fields']['timeoriginalestimate'] ?? 0);
            }
        }





        return ['projects' => $groupedIssues, 'users' => $users_data, 'warningList' => $user_warning];
    }

    public function getDetailIssueByKey($issueKey)
    {
        $issueResponse = $this->client->get("/rest/api/3/issue/{$issueKey}?expand=changelog");
        $issueDetail = json_decode($issueResponse->getBody()->getContents(), true);
        return $issueDetail;
    }
    public function getDetailsProjectsById($id)
    {
        $response = $this->client->get('/rest/api/3/project/' . $id);
        return json_decode($response->getBody()->getContents(), true);
    }
    public function getAllBoardByIdProjects($id)
    {
        $response = $this->client->get('/rest/agile/1.0/board?projectKeyOrId=' . $id);
        return json_decode($response->getBody()->getContents(), true);
    }
    public function getAllSprintByIdBoard($id)
    {
        $response = $this->client->get('/rest/agile/1.0/board/' . $id . '/sprint');
        return json_decode($response->getBody()->getContents(), true);
    }
    public function getAllIssueByIdSprint($id)
    {
        $response = $this->client->get('/rest/agile/1.0/sprint/' . $id . '/issue');
        return json_decode($response->getBody()->getContents(), true);
    }
}
