import { logger } from "../logger/index.mjs";
import { getDBClient } from "../utils/mongoDB/connectDB.mjs";
import { configurationFilesCandidates } from "@intlayer/config";
import { ObjectId } from "mongodb";
import { Octokit } from "@octokit/rest";

//#region src/services/github.service.ts
const getAuthorizationUrl = (redirectUri, login) => {
	const clientId = process.env.GITHUB_CLIENT_ID;
	if (!clientId) throw new Error("GitHub Client ID is not configured");
	const params = new URLSearchParams({
		client_id: clientId,
		scope: "repo",
		state: "github_oauth",
		redirect_uri: redirectUri
	});
	if (login) params.append("login", login);
	return `https://github.com/login/oauth/authorize?${params.toString()}`;
};
const exchangeCodeForToken = async (code) => {
	const clientId = process.env.GITHUB_CLIENT_ID;
	const clientSecret = process.env.GITHUB_CLIENT_SECRET;
	if (!clientId || !clientSecret) throw new Error("GitHub OAuth credentials are not configured");
	try {
		const response = await fetch("https://github.com/login/oauth/access_token", {
			method: "POST",
			headers: {
				"Content-Type": "application/json",
				Accept: "application/json"
			},
			body: JSON.stringify({
				client_id: clientId,
				client_secret: clientSecret,
				code
			})
		});
		if (!response.ok) throw new Error(`GitHub token exchange failed: ${response.statusText}`);
		const data = await response.json();
		if (data.error) throw new Error(`GitHub token error: ${data.error_description}`);
		return data.access_token;
	} catch (error) {
		logger.error("Error exchanging GitHub code for token:", error);
		throw error;
	}
};
const getUserRepos = async (accessToken) => {
	try {
		const { data } = await new Octokit({ auth: accessToken }).rest.repos.listForAuthenticatedUser({
			sort: "updated",
			per_page: 100,
			visibility: "all"
		});
		return data;
	} catch (error) {
		logger.error("Error fetching GitHub repositories:", error);
		throw error;
	}
};
/**
* Check if valid intlayer configuration files exist in a repository (Recursively).
* Returns an array of file paths found (e.g. ['intlayer.config.ts', 'apps/web/intlayer.config.js']).
*/
const checkIntlayerConfig = async (accessToken, owner, repo, branch = "main") => {
	try {
		const { data } = await new Octokit({ auth: accessToken }).rest.git.getTree({
			owner,
			repo,
			tree_sha: branch,
			recursive: "true"
		});
		if (!data.tree || !Array.isArray(data.tree)) return [];
		return data.tree.filter((item) => {
			if (item.type !== "blob" || !item.path) return false;
			return configurationFilesCandidates.some((candidate) => item.path?.endsWith(candidate));
		}).map((item) => item.path);
	} catch (error) {
		if (error.status === 404 || error.status === 409) return [];
		logger.error("Error checking intlayer configuration:", error);
		return [];
	}
};
/**
* Get repository file contents and decode it
*/
const getRepositoryFileContents = async (accessToken, owner, repo, path, branch = "main") => {
	try {
		const { data } = await new Octokit({ auth: accessToken }).rest.repos.getContent({
			owner,
			repo,
			path,
			ref: branch
		});
		if (Array.isArray(data) || !("content" in data)) throw new Error("Path points to a directory, not a file");
		return Buffer.from(data.content, "base64").toString("utf-8");
	} catch (error) {
		if (error.status === 404) return null;
		logger.error("Error fetching repository file contents:", error);
		throw error;
	}
};
const getGitHubTokenFromUser = async (userId) => {
	try {
		const db = getDBClient().db();
		let account = await db.collection("account").findOne({
			userId,
			providerId: "github"
		});
		if (!account && ObjectId.isValid(userId)) account = await db.collection("account").findOne({
			userId: new ObjectId(userId),
			providerId: "github"
		});
		if (!account) account = await db.collection("accounts").findOne({
			userId,
			providerId: "github"
		});
		if (!account && ObjectId.isValid(userId)) account = await db.collection("accounts").findOne({
			userId: new ObjectId(userId),
			providerId: "github"
		});
		if (!account) return null;
		return account.accessToken || account.access_token || null;
	} catch (error) {
		logger.error("Error retrieving GitHub token from DB:", error);
		return null;
	}
};
const triggerGithubDispatch = async ({ project, eventType = "intlayer_cms_update", payload = {} }) => {
	const { repository, oAuth2Access } = project;
	if (!repository || repository.provider !== "github") throw new Error("Project is not connected to a GitHub repository.");
	const accessToken = (oAuth2Access?.[0])?.accessToken?.[0];
	if (!accessToken) throw new Error("No valid OAuth2 access token found for GitHub.");
	const { owner, repository: repoName } = repository;
	const url = `https://api.github.com/repos/${owner}/${repoName}/dispatches`;
	try {
		const response = await fetch(url, {
			method: "POST",
			headers: {
				Authorization: `Bearer ${accessToken}`,
				Accept: "application/vnd.github+json",
				"Content-Type": "application/json"
			},
			body: JSON.stringify({
				event_type: eventType,
				client_payload: {
					...payload,
					projectId: project.id,
					timestamp: Date.now()
				}
			})
		});
		if (!response.ok) {
			const errorText = await response.text();
			throw new Error(`GitHub API Error: ${response.status} - ${errorText}`);
		}
		logger.info(`Successfully triggered GitHub Action '${eventType}' for ${owner}/${repoName}`);
		return true;
	} catch (error) {
		logger.error(error);
		throw error;
	}
};
/**
* Check if a GitHub workflow file exists
*/
const checkWorkflowFileExists = async (accessToken, owner, repo, filename, branch = "main") => {
	try {
		await new Octokit({ auth: accessToken }).rest.repos.getContent({
			owner,
			repo,
			path: filename,
			ref: branch
		});
		return true;
	} catch (error) {
		if (error.status === 404) return false;
		logger.error("Error checking workflow file existence:", error);
		throw error;
	}
};
/**
* Create or update a GitHub workflow file
*/
const createWorkflowFile = async (accessToken, owner, repo, filename, content, branch = "main", message = "Add Intlayer CI workflow") => {
	try {
		const octokit = new Octokit({ auth: accessToken });
		let sha;
		try {
			const { data } = await octokit.rest.repos.getContent({
				owner,
				repo,
				path: filename,
				ref: branch
			});
			if (Array.isArray(data) || !("sha" in data)) throw new Error("Path points to a directory, not a file");
			sha = data.sha;
		} catch (error) {
			if (error.status !== 404) throw error;
		}
		const encodedContent = Buffer.from(content, "utf-8").toString("base64");
		await octokit.rest.repos.createOrUpdateFileContents({
			owner,
			repo,
			path: filename,
			message,
			content: encodedContent,
			branch,
			...sha && { sha }
		});
		logger.info(`Successfully ${sha ? "updated" : "created"} workflow file ${filename} for ${owner}/${repo}`);
	} catch (error) {
		logger.error("Error creating/updating workflow file:", error);
		throw error;
	}
};

//#endregion
export { checkIntlayerConfig, checkWorkflowFileExists, createWorkflowFile, exchangeCodeForToken, getAuthorizationUrl, getGitHubTokenFromUser, getRepositoryFileContents, getUserRepos, triggerGithubDispatch };
//# sourceMappingURL=github.service.mjs.map