/**
 * Script pour migrer les fichiers R2 de neoad/clients/annonceurs/ vers clients/annonceurs/
 * et mettre à jour les URLs dans la base de données
 */

import { S3Client, ListObjectsV2Command, CopyObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
import Surreal from 'surrealdb';
import 'dotenv/config';

const R2_ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID!;
const R2_ACCESS_KEY_ID = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID!;
const R2_SECRET_ACCESS_KEY = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY!;
const R2_BUCKET_NAME = process.env.CLOUDFLARE_R2_BUCKET_NAME || 'neoad';
const R2_PUBLIC_URL = process.env.CLOUDFLARE_R2_PUBLIC_URL!;

const s3Client = new S3Client({
	region: 'auto',
	endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
	credentials: {
		accessKeyId: R2_ACCESS_KEY_ID,
		secretAccessKey: R2_SECRET_ACCESS_KEY
	}
});

async function listAllObjects(prefix: string): Promise<{ Key: string; Size: number }[]> {
	const objects: { Key: string; Size: number }[] = [];
	let continuationToken: string | undefined;

	do {
		const command = new ListObjectsV2Command({
			Bucket: R2_BUCKET_NAME,
			Prefix: prefix,
			ContinuationToken: continuationToken
		});

		const response = await s3Client.send(command);
		
		if (response.Contents) {
			for (const obj of response.Contents) {
				if (obj.Key && obj.Size !== undefined) {
					objects.push({ Key: obj.Key, Size: obj.Size });
				}
			}
		}

		continuationToken = response.NextContinuationToken;
	} while (continuationToken);

	return objects;
}

async function copyObject(sourceKey: string, destKey: string): Promise<void> {
	const command = new CopyObjectCommand({
		Bucket: R2_BUCKET_NAME,
		CopySource: `${R2_BUCKET_NAME}/${sourceKey}`,
		Key: destKey
	});

	await s3Client.send(command);
	console.log(`  Copié: ${sourceKey} -> ${destKey}`);
}

async function deleteObject(key: string): Promise<void> {
	const command = new DeleteObjectCommand({
		Bucket: R2_BUCKET_NAME,
		Key: key
	});

	await s3Client.send(command);
	console.log(`  Supprimé: ${key}`);
}

async function updateDatabaseUrls(db: Surreal, oldPrefix: string, newPrefix: string): Promise<number> {
	// Mettre à jour les URLs dans la table ad
	const adResult = await db.query<[any[]]>(`
		SELECT id, output_url, thumbnail_url, content FROM ad 
		WHERE output_url CONTAINS $oldPrefix 
		   OR thumbnail_url CONTAINS $oldPrefix
		   OR content.preview_url CONTAINS $oldPrefix
	`, { oldPrefix });

	const ads = adResult[0] || [];
	let updatedCount = 0;

	for (const ad of ads) {
		const updates: Record<string, any> = {};
		
		if (ad.output_url?.includes(oldPrefix)) {
			updates.output_url = ad.output_url.replace(oldPrefix, newPrefix);
		}
		if (ad.thumbnail_url?.includes(oldPrefix)) {
			updates.thumbnail_url = ad.thumbnail_url.replace(oldPrefix, newPrefix);
		}
		if (ad.content?.preview_url?.includes(oldPrefix)) {
			updates['content.preview_url'] = ad.content.preview_url.replace(oldPrefix, newPrefix);
		}

		if (Object.keys(updates).length > 0) {
			const adId = typeof ad.id === 'string' ? ad.id.replace('ad:', '') : ad.id?.id || ad.id;
			const setClause = Object.entries(updates)
				.map(([key, value]) => `${key} = "${value}"`)
				.join(', ');
			
			await db.query(`UPDATE type::thing("ad", $adId) SET ${setClause}`, { adId });
			updatedCount++;
			console.log(`  Mise à jour ad: ${adId}`);
		}
	}

	// Mettre à jour les URLs dans la table media
	const mediaResult = await db.query<[any[]]>(`
		SELECT id, url, thumbnail_url FROM media 
		WHERE url CONTAINS $oldPrefix 
		   OR thumbnail_url CONTAINS $oldPrefix
	`, { oldPrefix });

	const medias = mediaResult[0] || [];

	for (const media of medias) {
		const updates: Record<string, any> = {};
		
		if (media.url?.includes(oldPrefix)) {
			updates.url = media.url.replace(oldPrefix, newPrefix);
		}
		if (media.thumbnail_url?.includes(oldPrefix)) {
			updates.thumbnail_url = media.thumbnail_url.replace(oldPrefix, newPrefix);
		}

		if (Object.keys(updates).length > 0) {
			const mediaId = typeof media.id === 'string' ? media.id.replace('media:', '') : media.id?.id || media.id;
			const setClause = Object.entries(updates)
				.map(([key, value]) => `${key} = "${value}"`)
				.join(', ');
			
			await db.query(`UPDATE type::thing("media", $mediaId) SET ${setClause}`, { mediaId });
			updatedCount++;
			console.log(`  Mise à jour media: ${mediaId}`);
		}
	}

	return updatedCount;
}

async function main() {
	console.log('=== Migration des chemins R2 ===\n');

	// 1. Lister les objets dans l'ancien chemin
	console.log('1. Listage des fichiers dans neoad/clients/annonceurs/...');
	const oldObjects = await listAllObjects('neoad/clients/annonceurs/');
	
	if (oldObjects.length === 0) {
		console.log('   Aucun fichier trouvé dans neoad/clients/annonceurs/');
		console.log('   Vérification dans neoad/neoad/clients/annonceurs/...');
		
		const altObjects = await listAllObjects('neoad/neoad/clients/annonceurs/');
		if (altObjects.length > 0) {
			console.log(`   Trouvé ${altObjects.length} fichiers dans neoad/neoad/clients/annonceurs/`);
			oldObjects.push(...altObjects);
		}
	}

	console.log(`   Trouvé ${oldObjects.length} fichiers à migrer\n`);

	if (oldObjects.length === 0) {
		// Lister tout pour voir ce qu'il y a
		console.log('Listage de tous les objets dans le bucket...');
		const allObjects = await listAllObjects('');
		console.log(`Total: ${allObjects.length} objets`);
		for (const obj of allObjects.slice(0, 20)) {
			console.log(`  - ${obj.Key} (${obj.Size} bytes)`);
		}
		if (allObjects.length > 20) {
			console.log(`  ... et ${allObjects.length - 20} autres`);
		}
		return;
	}

	// 2. Copier les fichiers vers le nouveau chemin
	console.log('2. Copie des fichiers vers clients/annonceurs/...');
	for (const obj of oldObjects) {
		// Transformer neoad/clients/annonceurs/... en clients/annonceurs/...
		// ou neoad/neoad/clients/annonceurs/... en clients/annonceurs/...
		let newKey = obj.Key;
		if (newKey.startsWith('neoad/neoad/clients/')) {
			newKey = newKey.replace('neoad/neoad/clients/', 'clients/');
		} else if (newKey.startsWith('neoad/clients/')) {
			newKey = newKey.replace('neoad/clients/', 'clients/');
		}

		if (newKey !== obj.Key) {
			await copyObject(obj.Key, newKey);
		}
	}

	// 3. Mettre à jour les URLs dans la base de données
	console.log('\n3. Mise à jour des URLs dans la base de données...');
	
	const db = new Surreal();
	await db.connect(process.env.SURREAL_URL + '/rpc', {
		namespace: process.env.SURREAL_NAMESPACE,
		database: process.env.SURREAL_DATABASE,
		auth: {
			username: process.env.SURREAL_USER!,
			password: process.env.SURREAL_PASS!
		}
	});

	// Mettre à jour les deux patterns possibles
	let totalUpdates = 0;
	totalUpdates += await updateDatabaseUrls(db, 'neoad/neoad/clients/', 'clients/');
	totalUpdates += await updateDatabaseUrls(db, 'neoad/clients/', 'clients/');

	console.log(`   ${totalUpdates} enregistrements mis à jour\n`);

	// 4. Supprimer les anciens fichiers
	console.log('4. Suppression des anciens fichiers...');
	for (const obj of oldObjects) {
		await deleteObject(obj.Key);
	}

	await db.close();

	console.log('\n=== Migration terminée ===');
}

main().catch(console.error);
