import axios from "axios";
import { GithubRepository, GithubRepositoriesSchema, DeployKeysSchema, ListWebhooksResponseSchema } from "config";

export class GithubClient {
	private token: string;

	constructor(token: string) {
		this.token = token;
	}

	private getHeaders() {
		return {
			Authorization: `Bearer ${this.token}`,
			Accept: "application/vnd.github.v3+json",
			"X-GitHub-Api-Version": "2022-11-28",
		};
	}

	async getRepositories(): Promise<GithubRepository[]> {
		const allRepos: GithubRepository[] = [];
		let url: string | null = "https://api.github.com/user/repos?per_page=100";

		while (url) {
			const response = await axios.get(url, {
				headers: this.getHeaders(),
			});

			const reposOnPage = GithubRepositoriesSchema.parse(response.data);
			allRepos.push(...reposOnPage);

			const linkHeader = response.headers.link as string | undefined;
			if (linkHeader) {
				const nextLink = linkHeader.split(",").find((s) => s.includes('rel="next"'));
				if (nextLink) {
					const match = nextLink.match(/<(.*)>/);
					url = match ? match[1] : null;
				} else {
					url = null;
				}
			} else {
				url = null;
			}
		}
		return allRepos;
	}

	// TODO(giolekva): generate unique deploy key per repo and add it to the repo
	async addDeployKey(repoPath: string, key: string) {
		await this.addSSHKey(key);
		// 	const sshUrl = repoPath;
		// 	const repoOwnerAndName = sshUrl.replace("git@github.com:", "").replace(".git", "");
		// 	let existingKeys: DeployKeys = [];
		// 	const response = await axios.get(`https://api.github.com/repos/${repoOwnerAndName}/keys`, {
		// 		headers: this.getHeaders(),
		// 	});
		// 	const parsedResult = DeployKeysSchema.safeParse(response.data);
		// 	if (parsedResult.success) {
		// 		existingKeys = parsedResult.data;
		// 	} else {
		// 		console.error("Failed to parse existing deploy keys:", parsedResult.error);
		// 	}
		// 	const keyToAddParts = key.trim().split(" ");
		// 	const mainKeyPartToAdd = keyToAddParts.length > 1 ? keyToAddParts.slice(0, 2).join(" ") : key.trim();
		// 	const keyAlreadyExists = existingKeys.some((existingKeyObj) => {
		// 		const existingKeyParts = existingKeyObj.key.trim().split(" ");
		// 		const mainExistingKeyPart =
		// 			existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
		// 		return mainExistingKeyPart === mainKeyPartToAdd;
		// 	});
		// 	if (keyAlreadyExists) {
		// 		return;
		// 	}
		// 	await axios.post(
		// 		`https://api.github.com/repos/${repoOwnerAndName}/keys`,
		// 		{
		// 			title: "dodo",
		// 			key: key,
		// 			read_only: true,
		// 		},
		// 		{
		// 			headers: this.getHeaders(),
		// 		},
		// 	);
	}

	async removeDeployKey(repoPath: string, key: string) {
		this.removeSSHKey(key);
		// const sshUrl = repoPath;
		// const repoOwnerAndName = sshUrl.replace("git@github.com:", "").replace(".git", "");
		// const response = await axios.get(`https://api.github.com/repos/${repoOwnerAndName}/keys`, {
		// 	headers: this.getHeaders(),
		// });
		// const result = DeployKeysSchema.safeParse(response.data);
		// if (!result.success) {
		// 	console.error("Failed to parse deploy keys response for removal:", result.error);
		// 	// Depending on desired robustness, you might throw an error or return early.
		// 	// For now, if parsing fails, we can't identify keys to remove.
		// 	throw new Error("Failed to parse deploy keys response during removal process");
		// }

		// // Extract the main part of the key we intend to remove
		// const keyToRemoveParts = key.trim().split(" ");
		// const mainKeyPartToRemove = keyToRemoveParts.length > 1 ? keyToRemoveParts.slice(0, 2).join(" ") : key.trim();

		// const deployKeysToDelete = result.data.filter((existingKeyObj) => {
		// 	const existingKeyParts = existingKeyObj.key.trim().split(" ");
		// 	const mainExistingKeyPart =
		// 		existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
		// 	return mainExistingKeyPart === mainKeyPartToRemove;
		// });

		// if (deployKeysToDelete.length === 0) {
		// 	console.log(
		// 		`No deploy key matching '${mainKeyPartToRemove.substring(0, 50)}...' found in repo ${repoOwnerAndName} for removal.`,
		// 	);
		// 	return;
		// }

		// await Promise.all(
		// 	deployKeysToDelete.map((deployKey) => {
		// 		console.log(
		// 			`Removing deploy key ID ${deployKey.id} ('${deployKey.key.substring(0, 50)}...') from repo ${repoOwnerAndName}`,
		// 		);
		// 		return axios.delete(`https://api.github.com/repos/${repoOwnerAndName}/keys/${deployKey.id}`, {
		// 			headers: this.getHeaders(),
		// 		});
		// 	}),
		// );
		// console.log(
		// 	`Successfully initiated removal of ${deployKeysToDelete.length} matching deploy key(s) from ${repoOwnerAndName}.`,
		// );
	}

	async addPushWebhook(repoPath: string, callbackAddress: string): Promise<boolean> {
		const repoOwnerAndName = repoPath.replace("git@github.com:", "").replace(".git", "");
		const resp = await axios.post(
			`https://api.github.com/repos/${repoOwnerAndName}/hooks`,
			{
				name: "web",
				active: true,
				events: ["push"],
				config: {
					url: callbackAddress,
					content_type: "json",
				},
			},
			{
				headers: this.getHeaders(),
			},
		);
		return resp.status === 201;
	}

	async removePushWebhook(repoPath: string, callbackAddress: string): Promise<boolean> {
		const repoOwnerAndName = repoPath.replace("git@github.com:", "").replace(".git", "");
		const listHooksUrl = `https://api.github.com/repos/${repoOwnerAndName}/hooks`;
		try {
			const response = await axios.get(listHooksUrl, {
				headers: this.getHeaders(),
			});
			const parsedHooks = ListWebhooksResponseSchema.safeParse(response.data);
			if (!parsedHooks.success) {
				throw new Error(`Failed to parse webhooks list for ${repoOwnerAndName}`);
			}
			const results = await Promise.all(
				parsedHooks.data
					.filter((hook) => hook.config.url === callbackAddress && hook.events.includes("push"))
					.map(async (hook) => {
						const deleteHookUrl = `https://api.github.com/repos/${repoOwnerAndName}/hooks/${hook.id}`;
						const resp = await axios.delete(deleteHookUrl, { headers: this.getHeaders() });
						return resp.status === 204;
					}),
			);
			return results.reduce((acc, curr) => acc && curr, true);
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
		} catch (error: any) {
			console.error(
				`Failed to list or remove webhooks for ${repoOwnerAndName}:`,
				error.response?.data || error.message,
			);
			throw error; // Re-throw to let the caller handle it
		}
	}

	async addSSHKey(key: string) {
		let existingKeys: { key: string }[] = [];
		const response = await axios.get(`https://api.github.com/user/keys`, {
			headers: this.getHeaders(),
		});
		if (Array.isArray(response.data)) {
			existingKeys = response.data;
		}
		const keyToAddParts = key.trim().split(" ");
		const mainKeyPartToAdd = keyToAddParts.length > 1 ? keyToAddParts.slice(0, 2).join(" ") : key.trim();
		const keyAlreadyExists = existingKeys.some((existingKeyObj) => {
			const existingKeyParts = existingKeyObj.key.trim().split(" ");
			const mainExistingKeyPart =
				existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
			return mainExistingKeyPart === mainKeyPartToAdd;
		});

		if (keyAlreadyExists) {
			return;
		}
		await axios.post(
			`https://api.github.com/user/keys`,
			{
				title: "dodo",
				key: key,
			},
			{
				headers: this.getHeaders(),
			},
		);
	}

	async removeSSHKey(key: string) {
		const response = await axios.get(`https://api.github.com/user/keys`, {
			headers: this.getHeaders(),
		});

		const result = DeployKeysSchema.safeParse(response.data);
		if (!result.success) {
			throw new Error("Failed to parse deploy keys response during removal process");
		}

		// Extract the main part of the key we intend to remove
		const keyToRemoveParts = key.trim().split(" ");
		const mainKeyPartToRemove = keyToRemoveParts.length > 1 ? keyToRemoveParts.slice(0, 2).join(" ") : key.trim();

		const deployKeysToDelete = result.data.filter((existingKeyObj) => {
			const existingKeyParts = existingKeyObj.key.trim().split(" ");
			const mainExistingKeyPart =
				existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
			return mainExistingKeyPart === mainKeyPartToRemove;
		});

		if (deployKeysToDelete.length === 0) {
			return;
		}

		await Promise.all(
			deployKeysToDelete.map((deployKey) => {
				return axios.delete(`https://api.github.com/user/keys/${deployKey.id}`, {
					headers: this.getHeaders(),
				});
			}),
		);
	}
}
