/*******************************************************************************
 * Copyright (c) 2026, Jan Koester jan.koester@gmx.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 * Neither the name of the <organization> nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "authimageprovider.h"
#include "publishclient.h"

#include <QNetworkReply>
#include <QNetworkRequest>
#include <QQuickTextureFactory>
#include <QImage>
#include <QImageReader>
#include <QBuffer>
#include <QMetaObject>
#include <QDebug>

AuthImageResponse::AuthImageResponse(const QString &url, const QSize &requestedSize,
                                     PublishClient *pub)
{
    /*
     * requestImageResponse() is called from the QQuickPixmapReader thread,
     * but QNetworkAccessManager must be used from its owning (main) thread.
     * Dispatch the network request to the main thread via QueuedConnection.
     */
    QMetaObject::invokeMethod(pub, [this, url, requestedSize, pub]() {
        QNetworkAccessManager *nam = pub->networkManager();
        QUrl imageUrl = QUrl::fromEncoded(url.toUtf8());

        QNetworkRequest req(imageUrl);
        req.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
        req.setRawHeader("Accept", "image/png, image/jpeg, image/gif, image/bmp, image/webp, */*");

        QString token = pub->authToken();
        if (!token.isEmpty())
            req.setRawHeader("Cookie", ("authid=" + token).toUtf8());

        QNetworkReply *reply = nam->get(req);

        if (pub->ignoreSsl()) {
            connect(reply, &QNetworkReply::sslErrors,
                    reply, qOverload<>(&QNetworkReply::ignoreSslErrors));
        }

        /*
         * Use DirectConnection: reply lives on main thread, the lambda
         * runs on main thread, which is fine.  QQuickImageResponse::finished()
         * is documented as safe to emit from any thread.
         */
        connect(reply, &QNetworkReply::finished, this, [this, reply, requestedSize]() {
            reply->deleteLater();

            if (reply->error() != QNetworkReply::NoError) {
                _error = reply->errorString();
                emit finished();
                return;
            }

            QByteArray data = reply->readAll();
            if (data.isEmpty()) {
                _error = "Empty response from server";
                emit finished();
                return;
            }

            /* Derive format hint from Content-Type header */
            QByteArray formatHint;
            QString ct = reply->header(QNetworkRequest::ContentTypeHeader).toString().toLower();
            if (ct.contains("png"))        formatHint = "png";
            else if (ct.contains("jpeg") || ct.contains("jpg")) formatHint = "jpeg";
            else if (ct.contains("gif"))   formatHint = "gif";
            else if (ct.contains("webp"))  formatHint = "webp";
            else if (ct.contains("bmp"))   formatHint = "bmp";
            else if (ct.contains("svg"))   formatHint = "svg";

            /* Try with format hint first, then without (auto-detect) */
            if (!formatHint.isEmpty())
                _image = QImage::fromData(data, formatHint.constData());
            if (_image.isNull())
                _image = QImage::fromData(data);

            if (_image.isNull()) {
                /* Try QImageReader with explicit format probing */
                QBuffer buf(&data);
                buf.open(QIODevice::ReadOnly);
                QImageReader reader(&buf);
                reader.setDecideFormatFromContent(true);
                _image = reader.read();
            }

            if (_image.isNull()) {
                _error = "Cannot decode image (Content-Type: " + ct
                       + ", " + QString::number(data.size()) + " bytes, "
                       + "supported: " + QImageReader::supportedImageFormats().join(", ") + ")";
                qWarning() << "AuthImageProvider:" << _error;
                emit finished();
                return;
            }

            if (requestedSize.isValid() && !requestedSize.isEmpty())
                _image = _image.scaled(requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);

            emit finished();
        }, Qt::DirectConnection);
    }, Qt::QueuedConnection);
}

QQuickTextureFactory *AuthImageResponse::textureFactory() const {
    return _image.isNull() ? nullptr : QQuickTextureFactory::textureFactoryForImage(_image);
}

AuthImageProvider::AuthImageProvider(PublishClient *pub)
    : _pub(pub)
{
}

QQuickImageResponse *AuthImageProvider::requestImageResponse(
    const QString &id, const QSize &requestedSize)
{
    return new AuthImageResponse(id, requestedSize, _pub);
}
