/* SPDX-FileCopyrightText: 2026 Project Tick
 * SPDX-FileContributor: Project Tick
 * SPDX-License-Identifier: GPL-3.0-or-later WITH LicenseRef-MeshMC-MMCO-Module-Exception-1.0
 *
 *   MeshMC - A Custom Launcher for Minecraft
 *   Copyright (C) 2026 Project Tick
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version, with the additional permission
 *   described in the MeshMC MMCO Module Exception 1.0.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 *   You should have received a copy of the MeshMC MMCO Module Exception 1.0
 *   along with this program.  If not, see <https://projecttick.org/licenses/>.
 */

#pragma once

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QPair>

#include "Reply.h"
#include "RequestParameter.h"
#include "Bits.h"

namespace Katabasis
{

	class ReplyServer;
	class PollServer;

	/// Simple OAuth2 Device Flow authenticator.
	class DeviceFlow : public QObject
	{
		Q_OBJECT
	  public:
		Q_ENUMS(GrantFlow)

	  public:
		struct Options {
			QString userAgent = QStringLiteral("Katabasis/1.0");
			QString responseType = QStringLiteral("code");
			QString scope;
			QString clientIdentifier;
			QString clientSecret;
			QUrl authorizationUrl;
			QUrl accessTokenUrl;
		};

	  public:
		/// Are we authenticated?
		bool linked();

		/// Authentication token.
		QString token();

		/// Provider-specific extra tokens, available after a successful
		/// authentication
		QVariantMap extraTokens();

	  public:
		// TODO: put in `Options`
		/// User-defined extra parameters to append to request URL
		QVariantMap extraRequestParams();
		void setExtraRequestParams(const QVariantMap& value);

		// TODO: split up the class into multiple, each implementing one OAuth2
		// flow
		/// Grant type (if non-standard)
		QString grantType();
		void setGrantType(const QString& value);

	  public:
		/// Constructor.
		/// @param  parent  Parent object.
		explicit DeviceFlow(Options& opts, Token& token, QObject* parent = 0,
							QNetworkAccessManager* manager = 0);

		/// Get refresh token.
		QString refreshToken();

		/// Get token expiration time
		QDateTime expires();

	  public slots:
		/// Authenticate.
		void login();

		/// De-authenticate.
		void logout();

		/// Refresh token.
		bool refresh();

		/// Handle situation where reply server has opted to close its
		/// connection
		void serverHasClosed(bool paramsfound = false);

	  signals:
		/// Emitted when client needs to open a web browser window, with the
		/// given URL.
		void openBrowser(const QUrl& url);

		/// Emitted when client can close the browser window.
		void closeBrowser();

		/// Emitted when client needs to show a verification uri and user code
		void showVerificationUriAndCode(const QUrl& uri, const QString& code,
										int expiresIn);

		/// Emitted when the internal state changes
		void activityChanged(Activity activity);

	  public slots:
		/// Handle verification response.
		void onVerificationReceived(QMap<QString, QString>);

	  protected slots:
		/// Handle completion of a Device Authorization Request
		void onDeviceAuthReplyFinished();

		/// Handle completion of a refresh request.
		void onRefreshFinished();

		/// Handle failure of a refresh request.
		void onRefreshError(QNetworkReply::NetworkError error,
							QNetworkReply* reply);

	  protected:
		/// Set refresh token.
		void setRefreshToken(const QString& v);

		/// Set token expiration time.
		void setExpires(QDateTime v);

		/// Start polling authorization server
		void startPollServer(const QVariantMap& params, int expiresIn);

		/// Set authentication token.
		void setToken(const QString& v);

		/// Set the linked state
		void setLinked(bool v);

		/// Set extra tokens found in OAuth response
		void setExtraTokens(QVariantMap extraTokens);

		/// Set local poll server
		void setPollServer(PollServer* server);

		PollServer* pollServer() const;

		void updateActivity(Activity activity);

	  protected:
		Options options_;

		QVariantMap extraReqParams_;
		QNetworkAccessManager* manager_ = nullptr;
		ReplyList timedReplies_;
		QString grantType_;

	  protected:
		Token& token_;

	  private:
		PollServer* pollServer_ = nullptr;
		Activity activity_ = Activity::Idle;
	};

} // namespace Katabasis
