<?php

namespace mpyw\Cowitter\Helpers;

use mpyw\Cowitter\HttpException;
use mpyw\Cowitter\Response;
use mpyw\Cowitter\ResponseInterface;
use mpyw\Cowitter\Media;

class ResponseBodyDecoder
{
    protected $response;
    protected $info;
    protected $bodyBuffer;

    public static function getDecodedResponse(Response $response, $body_buffer = null)
    {
        $static = new static($response, $body_buffer);
        return $response->withDecodedContent($static->normalize());
    }

    protected function __construct(ResponseInterface $response, $body_buffer = null)
    {
        $this->response = $response;
        $this->info = curl_getinfo($response->getHandle());
        $this->bodyBuffer = $body_buffer === null ? $response->getRawContent() : $body_buffer;
    }

    protected function normalize()
    {
        if ($this->bodyBuffer === '') {
            return $this->handleEmptyBody();
        }
        if (preg_match('@\A(?:image|video)/@', $this->info['content_type'])) {
            return new Media($this->info['content_type'], $this->bodyBuffer);
        }
        if (null !== $obj = $this->handleJsonAndXml()) {
            return $obj;
        }
        if (strip_tags($this->bodyBuffer) === $this->bodyBuffer) {
            return $this->handleText();
        }
        if ($this->info['http_code'] >= 400 && preg_match("@<(?:pre|h1)>([^<]++)</(?:pre|h1)>@", $this->bodyBuffer, $matches)) {
            throw new HttpException(trim($matches[1]), -1, $this->response);
        }
        throw new \UnexpectedValueException('Malformed response detected: ' . $this->bodyBuffer);
    }

    protected function handleEmptyBody()
    {
        if ($this->info['http_code'] >= 200 && $this->info['http_code'] <= 204) {
            $message = 'Your request has been successfully sent. (This is a message generated by Cowitter)';
            return (object)['message' => $message];
        }
        throw new HttpException(
            "The server returned the status {$this->info['http_code']} with empty response. (This is a message generated by Cowitter)",
            -1,
            $this->response
        );
    }

    protected function handleJsonAndXml()
    {
        $orig = libxml_use_internal_errors(true);
        try {
            if (
                null  === $obj = json_decode(preg_replace('@\A/\*{2}/[^(]++\((.+)\);\z@s', '$1', $this->bodyBuffer)) and
                false === $obj = json_decode(json_encode(simplexml_load_string($this->bodyBuffer)))
            ) {
                return;
            }
        } finally {
            libxml_clear_errors();
            libxml_use_internal_errors($orig);
        }
        if (!empty($obj->error)) {
            $errors = $obj->error;
        } elseif (!empty($obj->errors)) {
            $errors = $obj->errors;
        } else {
            return $obj;
        }
        if (!is_array($errors)) {
            $errors = [$errors];
        }
        $messages = [];
        $code = -1;
        foreach ($errors as $error) {
            if (isset($error->code)) {
                $code = (int)filter_var($error->code);
            }
            $messages[] = (string)filter_var(isset($error->message) ? $error->message : $error);
        }
        throw new HttpException(implode("\n", $messages), $code, $this->response);
    }

    protected function handleText()
    {
        parse_str($this->bodyBuffer, $obj);
        $obj = (object)$obj;
        if (isset($obj->oauth_token, $obj->oauth_token_secret)) {
            return $obj;
        }
        throw new HttpException(trim($this->bodyBuffer), -1, $this->response);
    }
}
