<?php

/**
 * Class Curl
 *
 * HTTP client wrapper for making HTTP requests.
 */
class Curl
{
    private string $url;
    private $ch;
    private int $status_code;

    /**
     * Curl constructor.
     *
     * @param string $url The URL to request
     * @throws Exception If URL is empty
     */
    public function __construct(string $url)
    {
        if (empty($url)) {
            throw new Exception("URL cannot be empty.");
        }

        $this->url = $url;
        $this->ch = curl_init();
    }

    /**
     * Returns the HTTP status code.
     *
     * @return int The HTTP status code
     */
    public function getStatusCode(): int
    {
        return $this->status_code;
    }

    /**
     * Sets URL parameters.
     *
     * @param array $parameters Query parameters
     * @return Curl
     */
    public function setParameters(array $parameters): Curl
    {
        $separator = strpos($this->url, '?') ? '&' : '?';
        $this->url .= $separator . http_build_query($parameters);

        return $this;
    }

    /**
     * Sets HTTP headers.
     *
     * @param array $headers Headers to set
     * @param bool $header Whether to include header in output
     * @return $this
     */
    public function setHeaders(array $headers, bool $header = false): Curl
    {
        curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($this->ch, CURLOPT_HEADER, $header);

        return $this;
    }

    /**
     * Sets SSL verification.
     *
     * @param bool $verify Whether to verify SSL
     * @return Curl
     */
    public function verificarSsl(bool $verify = false): Curl
    {
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $verify);
        curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $verify);

        return $this;
    }

    /**
     * Sets up a POST request.
     *
     * @param array $fields POST fields
     * @return Curl
     */
    public function setPost(array $fields = []): Curl
    {
        curl_setopt($this->ch, CURLOPT_POST, true);
        if (!empty($fields)) {
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($fields));
        }

        return $this;
    }

    /**
     * Sets up a JSON POST request.
     *
     * @param array $fields Data to send as JSON
     * @return Curl
     * @throws Exception If fields are empty
     */
    public function setJsonPost(array $fields): Curl
    {
        if (empty($fields)) {
            throw new Exception("Error: empty fields");
        }

        curl_setopt($this->ch, CURLOPT_POST, true);
        curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($fields));

        return $this;
    }

    /**
     * Adds POST fields.
     *
     * @param array $fields Fields to add
     * @return $this
     * @throws Exception If fields are empty
     */
    public function addPost(array $fields): Curl
    {
        if (empty($fields)) {
            throw new Exception("Error: empty fields");
        }

        curl_setopt($this->ch, CURLOPT_POST, true);
        curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($fields));

        return $this;
    }

    /**
     * Sets up a DELETE request.
     *
     * @param array $fields Optional fields to send
     * @return Curl
     */
    public function setDelete(array $fields = []): Curl
    {
        curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, "DELETE");

        if (!empty($fields)) {
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($fields));
        }

        return $this;
    }

    /**
     * Executes the request and returns the response.
     *
     * @return bool|string The response
     */
    public function get()
    {
        curl_setopt($this->ch, CURLOPT_URL, $this->url);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($this->ch);
        $error = curl_error($this->ch);
        $errno = curl_errno($this->ch);

        $this->status_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);

        if (0 !== $errno) {
            throw new \RuntimeException($error, $errno);
        }

        return $response;
    }

    /**
     * Downloads a file from the URL.
     *
     * @param string $filepath Destination file path
     * @throws Exception On error
     */
    function downloadFile(string $filepath): void
    {
        if (empty($filepath)) {
            throw new Exception("File path cannot be empty.");
        }

        if (!filter_var($this->url, FILTER_VALIDATE_URL)) {
            throw new Exception("Invalid URL provided.");
        }

        if (($file = fopen($filepath, 'w+')) === false) {
            throw new Exception("Cannot open file: $filepath");
        }

        curl_setopt($this->ch, CURLOPT_URL, $this->url);
        curl_setopt($this->ch, CURLOPT_TIMEOUT, 300);
        curl_setopt($this->ch, CURLOPT_FILE, $file);
        curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true);

        curl_exec($this->ch);

        fclose($file);

        if (curl_errno($this->ch)) {
            throw new Exception(curl_error($this->ch));
        }
    }

    /**
     * Destructor - closes cURL handle.
     */
    public function __destruct()
    {
        if (is_resource($this->ch)) {
            \curl_close($this->ch);
        }
    }
}
