<?php

namespace App\Service;

use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
 * Wrapper API Sentry pour gerer les projets, issues et DSN programmatiquement.
 *
 * @codeCoverageIgnore
 */
class SentryService
{
    private const PATH_PROJECTS = '/projects/';
    private const PATH_ISSUES = '/issues/';

    public function __construct(
        private HttpClientInterface $httpClient,
        private LoggerInterface $logger,
        #[Autowire(env: 'SENTRY_AUTH_TOKEN')] private string $authToken = '',
        #[Autowire(env: 'SENTRY_ORG')] private string $defaultOrg = '',
        #[Autowire(env: 'SENTRY_API_URL')] private string $apiUrl = 'https://sentry.io/api/0',
    ) {
    }

    public function isAvailable(): bool
    {
        return '' !== $this->authToken;
    }

    /**
     * Cree un projet Sentry dans une equipe.
     *
     * @return array<string, mixed>|null
     */
    public function createProject(string $teamSlug, string $name, string $org = ''): ?array
    {
        $org = $this->resolveOrg($org);

        return $this->request('POST', '/teams/'.$org.'/'.$teamSlug.self::PATH_PROJECTS, ['name' => $name]);
    }

    public function deleteProject(string $projectSlug, string $org = ''): bool
    {
        $org = $this->resolveOrg($org);

        return null !== $this->request('DELETE', self::PATH_PROJECTS.$org.'/'.$projectSlug.'/');
    }

    /**
     * Recupere le DSN public du projet.
     */
    public function getProjectDsn(string $projectSlug, string $org = ''): ?string
    {
        $org = $this->resolveOrg($org);
        $result = $this->request('GET', self::PATH_PROJECTS.$org.'/'.$projectSlug.'/keys/');

        if (null === $result || !isset($result[0]['dsn']['public'])) {
            return null;
        }

        return (string) $result[0]['dsn']['public'];
    }

    /**
     * Liste tous les projets de l'organisation.
     *
     * @return list<array<string, mixed>>
     */
    public function listProjects(string $org = ''): array
    {
        $org = $this->resolveOrg($org);

        /* @var list<array<string, mixed>> */
        return $this->request('GET', '/organizations/'.$org.self::PATH_PROJECTS) ?? [];
    }

    /**
     * Liste les issues/erreurs d'un projet.
     *
     * @return list<array<string, mixed>>
     */
    public function listProjectErrors(string $projectSlug, ?string $query = null, string $org = ''): array
    {
        $org = $this->resolveOrg($org);
        $path = self::PATH_PROJECTS.$org.'/'.$projectSlug.self::PATH_ISSUES;

        if (null !== $query && '' !== $query) {
            $path .= '?query='.urlencode($query);
        }

        /* @var list<array<string, mixed>> */
        return $this->request('GET', $path) ?? [];
    }

    /**
     * Recupere les details d'une issue.
     *
     * @return array<string, mixed>|null
     */
    public function getIssueDetails(string $issueId): ?array
    {
        return $this->request('GET', self::PATH_ISSUES.$issueId.'/');
    }

    /**
     * Marque une issue comme resolue.
     */
    public function resolveIssue(string $issueId): bool
    {
        return null !== $this->request('PUT', self::PATH_ISSUES.$issueId.'/', ['status' => 'resolved']);
    }

    /**
     * Supprime une issue.
     */
    public function deleteIssue(string $issueId): bool
    {
        return null !== $this->request('DELETE', self::PATH_ISSUES.$issueId.'/');
    }

    /**
     * @param array<string, mixed>|null $body
     *
     * @return array<int|string, mixed>|null
     */
    private function request(string $method, string $path, ?array $body = null): ?array
    {
        if (!$this->isAvailable()) {
            $this->logger->warning('SentryService: auth token non configure');

            return null;
        }

        try {
            return $this->executeRequest($method, $path, $body);
        } catch (\Throwable $e) {
            $this->logger->error('SentryService: '.$e->getMessage(), ['method' => $method, 'path' => $path]);

            return null;
        }
    }

    /**
     * @param array<string, mixed>|null $body
     *
     * @return array<int|string, mixed>|null
     */
    private function executeRequest(string $method, string $path, ?array $body): ?array
    {
        $options = ['headers' => ['Authorization' => 'Bearer '.$this->authToken, 'Content-Type' => 'application/json']];
        if (null !== $body) {
            $options['json'] = $body;
        }

        $response = $this->httpClient->request($method, rtrim($this->apiUrl, '/').$path, $options);
        $statusCode = $response->getStatusCode();

        if ($statusCode >= 400) {
            $this->logger->error('SentryService: HTTP '.$statusCode.' '.$method.' '.$path, ['body' => $response->getContent(false)]);

            return null;
        }

        return (204 === $statusCode || '' === $response->getContent(false)) ? [] : $response->toArray();
    }

    private function resolveOrg(string $org): string
    {
        return '' !== $org ? $org : $this->defaultOrg;
    }
}
