import { AlbumNotFoundException } from "../exceptions";
import type { GoogleAlbum } from "../interfaces";
import { apiClient } from "./axiosClient";

const RPCID = "snAcKc";
const BASE_URL = "https://photos.google.com/_/PhotosUi/data/batchexecute";

// Pre-compiled Regex for better performance
const RE_CANONICAL = /<link\s+rel="canonical"\s+href="([^"]+)"/;
const RE_KEY = /key=([^"&]+)/;

export const getData = async (
	id: string,
	key: string,
	token?: string,
): Promise<GoogleAlbum> => {
	const queryData = [[RPCID, JSON.stringify([id, token, null, key])]];

	const params = new URLSearchParams();
	params.append("f.req", JSON.stringify([queryData]));

	let data: string;
	try {
		const res = await apiClient.post(BASE_URL, params, {
			headers: {
				"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
			},
		});
		data = res.data.replace(/^\)\]\}'/, ""); // Faster regex replacement
	} catch (error: any) {
		console.error(error?.message || error);
		throw error;
	}

	const list = JSON.parse(data);
	const reponseData = list.find((item: any[]) => item?.[1] === RPCID)?.[2];

	if (!reponseData) return {} as GoogleAlbum;

	const parsedResponse = JSON.parse(reponseData);

	const albumItems = parsedResponse?.[1] || [];
	const next = parsedResponse?.[2];
	const albumDetails = parsedResponse?.[3] || [];

	const [albumId, albumTitle, albumDates, downloadUrl, albumCover, albumOwner] =
		albumDetails;
	const sharedUrl = albumDetails?.[32];
	const createdAt = albumDates?.[4];
	const updatedAt = albumDates?.[8];

	const coverUrl = albumCover?.[0];
	const coverWidth = albumCover?.[1];
	const coverHeight = albumCover?.[2];

	const ownerId = albumOwner?.[1];
	const ownerDetails = albumOwner?.[11] || [];
	const [ownerFullname, , ownerGenre, ownerName] = ownerDetails;
	const ownerPhoto = albumOwner?.[3];

	const items = albumItems.map((item: any[]) => {
		const [id, itemData] = item;
		const [url] = itemData as any[];
		return { id, url };
	});

	return {
		id: albumId,
		key,
		token: next,
		title: albumTitle,
		url: sharedUrl,
		downloadUrl,
		createdAt,
		updatedAt,
		cover: {
			url: coverUrl,
			width: coverWidth,
			height: coverHeight,
		},
		owner: {
			id: ownerId,
			fullname: ownerFullname,
			name: ownerName,
			genre: ownerGenre,
			photo: ownerPhoto,
		},
		items,
	};
};

export const getAlbum = async (url: string): Promise<GoogleAlbum> => {
	let data = null;
	try {
		const res = await apiClient.get(url);
		data = res.data;
	} catch (error: any) {
		if (error?.response?.status === 404 || error?.code === "ERR_BAD_REQUEST") {
			throw new AlbumNotFoundException();
		}
		throw error;
	}

	// Extract Metadata using Regex (No Cheerio)
	const canonicalMatch = data.match(RE_CANONICAL);
	const canonicalUrl = canonicalMatch ? canonicalMatch[1] : "";

	if (!canonicalUrl) throw new AlbumNotFoundException();

	const albumUrlObj = new URL(canonicalUrl);
	const pathParts = albumUrlObj.pathname.split("/");
	const id = pathParts[pathParts.length - 1] || pathParts[pathParts.length - 2]; // Handle trailing slash

	// Key might be in the original URL or need to be extracted from response if redirected
	// But usually it's in the query param of the canonical or initial URL.
	// We'll trust the canonical for ID, but for key we check the URL params of the Input URL or the Canonical.
	let key = albumUrlObj.searchParams.get("key");
	if (!key) {
		// Fallback: try to find key in the HTML if not in URL
		// (The original code assumed it was in the canonical URL params)
		// If the original logic worked with `albumUrl.searchParams.get('key')`, we stick to it.
		// But let's check the input `url` too just in case.
		try {
			const inputUrlObj = new URL(url);
			key = inputUrlObj.searchParams.get("key");
		} catch {}
	}

	// Last resort regex if key is still missing (sometimes encoded in JS vars)
	if (!key) {
		const keyMatch = data.match(RE_KEY);
		if (keyMatch) key = keyMatch[1];
	}

	if (!id || !key) throw new AlbumNotFoundException();

	let album: GoogleAlbum | undefined;
	let token: string | undefined;
	let pageCount = 0;
	const MAX_PAGES = 100; // Safety limit

	do {
		// Safety break
		if (pageCount++ > MAX_PAGES) break;

		const parts = await getData(id, key, token);

		if (!album) {
			// set once on first page
			if (!parts.id) {
				// If getData failed to parse on first try
				throw new AlbumNotFoundException();
			}
			const { title, owner, cover, downloadUrl, createdAt, updatedAt } = parts;
			album = {
				key,
				id,
				url,
				title,
				token,
				owner,
				cover,
				downloadUrl,
				createdAt,
				updatedAt,
				items: [],
			};
		}

		token = parts.token;
		if (parts.items) {
			album.items.push(...parts.items);
		}
	} while (token);

	return album!;
};
