#!/usr/bin/env bun

import { join } from "path";
import { $ } from "bun";
import { albums } from "../src/lib/list";

// Configuration
const API_BASE = "https://api.song.link/v1-alpha.1/links";
const RATE_LIMIT_DELAY = 6000; // 6 seconds (10 requests per minute)
const USER_COUNTRY = "DE"; // Germany
const DATA_DIR = join(import.meta.dir, "../data");
const CACHE_FILE = join(DATA_DIR, "fetched-songs.json");
const OUTPUT_TS_FILE = join(import.meta.dir, "../src/lib/songLinks.ts");
const OUTPUT_JSON_FILE = join(DATA_DIR, "song-links.json");

interface SongLinks {
	spotify?: string;
	youtube?: string;
	youtubeMusic?: string;
	appleMusic?: string;
}

interface SongLinksData {
	[songId: string]: SongLinks;
}

interface CachedSongs {
	[songId: string]: boolean;
}

// Delay function for rate limiting
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Load cached songs
async function loadCache(): Promise<CachedSongs> {
	const file = Bun.file(CACHE_FILE);
	const exists = await file.exists();

	if (!exists) {
		console.log("No cache file found, starting fresh...");
		return {};
	}

	try {
		return await file.json();
	} catch {
		console.log("Invalid cache file, starting fresh...");
		return {};
	}
}

// Load existing song links
async function loadExistingSongLinks(): Promise<SongLinksData> {
	const file = Bun.file(OUTPUT_JSON_FILE);
	const exists = await file.exists();

	if (!exists) {
		console.log("No existing song links found, starting fresh...");
		return {};
	}

	try {
		return await file.json();
	} catch {
		console.log("Invalid song links file, starting fresh...");
		return {};
	}
}

// Ensure data directory exists
async function ensureDataDir(): Promise<void> {
	await Bun.write(join(DATA_DIR, ".gitkeep"), "");
}

// Save cache
async function saveCache(cache: CachedSongs): Promise<void> {
	await ensureDataDir();
	await Bun.write(CACHE_FILE, JSON.stringify(cache, null, 2));
}

// Save song links to JSON
async function saveSongLinksJSON(songLinks: SongLinksData): Promise<void> {
	await ensureDataDir();
	await Bun.write(OUTPUT_JSON_FILE, JSON.stringify(songLinks, null, 2));
}

// Save song links to TypeScript file
async function saveSongLinksTS(songLinks: SongLinksData): Promise<void> {
	const tsContent = `// This file is auto-generated by scripts/fetch-song-links.ts
// Do not edit manually
// Last updated: ${new Date().toISOString()}
// Run: bun run fetch-links

export interface SongLinks {
	spotify?: string;
	youtube?: string;
	youtubeMusic?: string;
	appleMusic?: string;
}

export interface SongLinksData {
	[songId: string]: SongLinks;
}

export function getSongLink(songId: string): SongLinks {
	return songLinks[songId];
}

export const songLinks: SongLinksData = ${JSON.stringify(songLinks, null, 2)};
`;

	await Bun.write(OUTPUT_TS_FILE, tsContent);
}

// Fetch song links from API
async function fetchSongLinks(spotifyUrl: string, songName: string): Promise<SongLinks | null> {
	try {
		// Encode the Spotify URL
		const encodedUrl = encodeURIComponent(spotifyUrl);

		// Use the url parameter with songIfSingle for better matching
		const url = `${API_BASE}?url=${encodedUrl}&userCountry=${USER_COUNTRY}&songIfSingle=true`;

		console.log(`  Fetching: ${songName}`);
		console.log(`  Spotify URL: ${spotifyUrl}`);

		const response = await fetch(url);

		if (!response.ok) {
			const errorText = await response.text();
			console.error(`  ❌ Failed to fetch (${response.status}): ${errorText}`);
			return null;
		}

		const data = await response.json();

		// Extract links from the response
		const links: SongLinks = {
			spotify: data.linksByPlatform?.spotify?.url || undefined,
			youtube: data.linksByPlatform?.youtube?.url || undefined,
			youtubeMusic: data.linksByPlatform?.youtubeMusic?.url || undefined,
			appleMusic: data.linksByPlatform?.appleMusic?.url || undefined,
		};

		console.log(`  ✓ Success!`);
		return links;
	} catch (error) {
		console.error(`  ❌ Error:`, error);
		return null;
	}
}

// Main function
async function main() {
	console.log("🎵 Starting song link fetcher...\n");

	// Load existing data
	const cache = await loadCache();
	const songLinks = await loadExistingSongLinks();

	let requestCount = 0;
	let successCount = 0;
	let skipCount = 0;
	let failCount = 0;

	// Process all albums and tracks
	for (const album of albums) {
		console.log(`\n📀 Album: ${album.label}`);

		for (const track of album.tracks) {
			const songId = track.id;
			const songName = track.label;
			const spotifyUrl = track.__SPOTIFY_URL__;

			// Skip if already fetched
			if (cache[songId]) {
				console.log(`  ⏭️  Skipping: ${songName} (already fetched)`);
				skipCount++;
				continue;
			}

			// Skip if no Spotify URL
			if (!spotifyUrl) {
				console.log(`  ⏭️  Skipping: ${songName} (no Spotify URL)`);
				skipCount++;
				continue;
			}

			// Add delay before request (except for first request)
			if (requestCount > 0) {
				console.log(`  ⏳ Waiting ${RATE_LIMIT_DELAY / 1000}s (rate limit)...`);
				await delay(RATE_LIMIT_DELAY);
			}

			// Fetch links
			const links = await fetchSongLinks(spotifyUrl, songName);

			if (links) {
				songLinks[songId] = links;
				cache[songId] = true;
				successCount++;

				// Save progress after each successful fetch
				await saveSongLinksJSON(songLinks);
				await saveCache(cache);
			} else {
				failCount++;
			}

			requestCount++;
		}
	}

	// Save final TypeScript file
	console.log("\n💾 Generating TypeScript file...");
	await saveSongLinksTS(songLinks);

	// Summary
	console.log("\n" + "=".repeat(50));
	console.log("📊 Summary:");
	console.log(`  Total requests: ${requestCount}`);
	console.log(`  Successful: ${successCount}`);
	console.log(`  Skipped: ${skipCount}`);
	console.log(`  Failed: ${failCount}`);
	console.log("=".repeat(50));

	// Format files with prettier
	console.log("\n🎨 Formatting files with Prettier...");
	try {
		await $`bunx prettier --write ${OUTPUT_TS_FILE} ${OUTPUT_JSON_FILE} ${CACHE_FILE}`;
		console.log(`  ✓ Formatted all files`);
	} catch (error) {
		console.error("  ⚠️  Prettier formatting failed:", error);
	}

	console.log("\n✨ Done!");
	console.log(`\n📁 Output files:`);
	console.log(`  - ${OUTPUT_TS_FILE}`);
	console.log(`  - ${OUTPUT_JSON_FILE}`);
	console.log(`  - ${CACHE_FILE}`);
}

// Run the script
main().catch((error) => {
	console.error("❌ Fatal error:", error);
	process.exit(1);
});
