import { v2 as cloudinary } from 'cloudinary'
import { prisma } from './database'
import { extractPublicId } from './cloudinary'
import { formatBytes } from '$lib/utils/format'

export { formatBytes }

export interface CloudinaryResource {
	public_id: string
	secure_url: string
	resource_type: string
	type: string
	format: string
	version: number
	width?: number
	height?: number
	bytes: number
	created_at: string
	folder?: string
}

export interface AuditResult {
	totalCloudinaryFiles: number
	totalDatabaseReferences: number
	orphanedFiles: CloudinaryResource[]
	orphanedTotalBytes: number
	missingFromCloudinary: string[]
}

/**
 * Fetches all resources from Cloudinary with pagination
 */
export async function fetchAllCloudinaryResources(): Promise<CloudinaryResource[]> {
	const resources: CloudinaryResource[] = []
	let nextCursor: string | undefined

	do {
		try {
			const result = await cloudinary.api.resources({
				type: 'upload',
				max_results: 500,
				next_cursor: nextCursor
			})

			resources.push(...result.resources)
			nextCursor = result.next_cursor
		} catch (error) {
			console.error('Error fetching Cloudinary resources:', error)
			throw error
		}
	} while (nextCursor)

	return resources
}

/**
 * Gets all Cloudinary URLs/public IDs referenced in the database
 */
export async function fetchAllDatabaseCloudinaryReferences(): Promise<Set<string>> {
	const publicIds = new Set<string>()

	// Get all Media table URLs
	const mediaRecords = await prisma.media.findMany({
		select: {
			url: true,
			thumbnailUrl: true
		}
	})

	for (const media of mediaRecords) {
		if (media.url?.includes('cloudinary.com')) {
			const publicId = extractPublicId(media.url)
			if (publicId) publicIds.add(publicId)
		}
		if (media.thumbnailUrl?.includes('cloudinary.com')) {
			const publicId = extractPublicId(media.thumbnailUrl)
			if (publicId) publicIds.add(publicId)
		}
	}

	// Get Project images
	const projects = await prisma.project.findMany({
		select: {
			featuredImage: true,
			logoUrl: true,
			gallery: true
		}
	})

	for (const project of projects) {
		if (project.featuredImage?.includes('cloudinary.com')) {
			const publicId = extractPublicId(project.featuredImage)
			if (publicId) publicIds.add(publicId)
		}
		if (project.logoUrl?.includes('cloudinary.com')) {
			const publicId = extractPublicId(project.logoUrl)
			if (publicId) publicIds.add(publicId)
		}
		if (project.gallery && typeof project.gallery === 'object') {
			const gallery = project.gallery as any[]
			for (const item of gallery) {
				if (item.url?.includes('cloudinary.com')) {
					const publicId = extractPublicId(item.url)
					if (publicId) publicIds.add(publicId)
				}
			}
		}
	}

	// Get Post images
	const posts = await prisma.post.findMany({
		select: {
			featuredImage: true,
			attachments: true
		}
	})

	for (const post of posts) {
		if (post.featuredImage?.includes('cloudinary.com')) {
			const publicId = extractPublicId(post.featuredImage)
			if (publicId) publicIds.add(publicId)
		}
		if (post.attachments && typeof post.attachments === 'object') {
			const attachments = post.attachments as any[]
			for (const attachment of attachments) {
				if (attachment.url?.includes('cloudinary.com')) {
					const publicId = extractPublicId(attachment.url)
					if (publicId) publicIds.add(publicId)
				}
			}
		}
	}

	return publicIds
}

/**
 * Performs a comprehensive audit of Cloudinary resources vs database references
 */
export async function auditCloudinaryResources(): Promise<AuditResult> {
	console.log('Starting Cloudinary audit...')

	// Fetch all resources from Cloudinary
	const cloudinaryResources = await fetchAllCloudinaryResources()
	console.log(`Found ${cloudinaryResources.length} files in Cloudinary`)

	// Fetch all database references
	const databasePublicIds = await fetchAllDatabaseCloudinaryReferences()
	console.log(`Found ${databasePublicIds.size} Cloudinary references in database`)

	// Find orphaned files (in Cloudinary but not in database)
	const orphanedFiles: CloudinaryResource[] = []
	let orphanedTotalBytes = 0

	for (const resource of cloudinaryResources) {
		// Skip thumbnails generated by Cloudinary (they have specific naming patterns)
		if (resource.public_id.includes('_thumbnail_')) {
			continue
		}

		if (!databasePublicIds.has(resource.public_id)) {
			orphanedFiles.push(resource)
			orphanedTotalBytes += resource.bytes || 0
		}
	}

	// Find missing files (in database but not in Cloudinary)
	const cloudinaryPublicIds = new Set(cloudinaryResources.map((r) => r.public_id))
	const missingFromCloudinary: string[] = []

	for (const publicId of databasePublicIds) {
		if (!cloudinaryPublicIds.has(publicId)) {
			missingFromCloudinary.push(publicId)
		}
	}

	return {
		totalCloudinaryFiles: cloudinaryResources.length,
		totalDatabaseReferences: databasePublicIds.size,
		orphanedFiles,
		orphanedTotalBytes,
		missingFromCloudinary
	}
}

/**
 * Deletes orphaned files from Cloudinary
 */
export async function deleteOrphanedFiles(
	publicIds: string[],
	dryRun = true
): Promise<{
	attempted: number
	succeeded: number
	failed: string[]
}> {
	const results = {
		attempted: publicIds.length,
		succeeded: 0,
		failed: [] as string[]
	}

	if (dryRun) {
		console.log(`DRY RUN: Would delete ${publicIds.length} files`)
		return { ...results, succeeded: publicIds.length }
	}

	for (const publicId of publicIds) {
		try {
			await cloudinary.uploader.destroy(publicId)
			results.succeeded++
			console.log(`Deleted: ${publicId}`)
		} catch (error) {
			results.failed.push(publicId)
			console.error(`Failed to delete ${publicId}:`, error)
		}
	}

	return results
}

/**
 * Cleans up broken references from the database
 */
export async function cleanupBrokenReferences(publicIds: string[]): Promise<{
	cleanedMedia: number
	cleanedProjects: number
	cleanedPosts: number
	errors: string[]
}> {
	const results = {
		cleanedMedia: 0,
		cleanedProjects: 0,
		cleanedPosts: 0,
		errors: [] as string[]
	}

	try {
		// Clean up Media table
		const mediaToClean = await prisma.media.findMany({
			where: {
				OR: [
					{ url: { contains: 'cloudinary.com' } },
					{ thumbnailUrl: { contains: 'cloudinary.com' } }
				]
			}
		})

		for (const media of mediaToClean) {
			let shouldDelete = false
			let updateThumbnail = false

			// Check if the main URL is broken
			if (media.url?.includes('cloudinary.com')) {
				const publicId = extractPublicId(media.url)
				if (publicId && publicIds.includes(publicId)) {
					// If the main URL is broken, we need to delete the entire record
					// since url is a required field
					shouldDelete = true
				}
			}

			// Check if only the thumbnail is broken
			if (!shouldDelete && media.thumbnailUrl?.includes('cloudinary.com')) {
				const publicId = extractPublicId(media.thumbnailUrl)
				if (publicId && publicIds.includes(publicId)) {
					updateThumbnail = true
				}
			}

			if (shouldDelete) {
				// Delete the media record entirely since the main URL is broken
				await prisma.media.delete({
					where: { id: media.id }
				})
				results.cleanedMedia++
			} else if (updateThumbnail) {
				// Only update the thumbnail to null if it's broken
				await prisma.media.update({
					where: { id: media.id },
					data: { thumbnailUrl: null }
				})
				results.cleanedMedia++
			}
		}

		// Clean up Project table
		const projectsToClean = await prisma.project.findMany({
			where: {
				OR: [
					{ featuredImage: { contains: 'cloudinary.com' } },
					{ logoUrl: { contains: 'cloudinary.com' } }
				]
			}
		})

		for (const project of projectsToClean) {
			let updated = false
			const updates: any = {}

			if (project.featuredImage?.includes('cloudinary.com')) {
				const publicId = extractPublicId(project.featuredImage)
				if (publicId && publicIds.includes(publicId)) {
					updates.featuredImage = null
					updated = true
				}
			}

			if (project.logoUrl?.includes('cloudinary.com')) {
				const publicId = extractPublicId(project.logoUrl)
				if (publicId && publicIds.includes(publicId)) {
					updates.logoUrl = null
					updated = true
				}
			}

			// Handle gallery items
			if (project.gallery && typeof project.gallery === 'object') {
				const gallery = project.gallery as any[]
				const cleanedGallery = gallery.filter((item) => {
					if (item.url?.includes('cloudinary.com')) {
						const publicId = extractPublicId(item.url)
						return !(publicId && publicIds.includes(publicId))
					}
					return true
				})

				if (cleanedGallery.length !== gallery.length) {
					updates.gallery = cleanedGallery
					updated = true
				}
			}

			if (updated) {
				await prisma.project.update({
					where: { id: project.id },
					data: updates
				})
				results.cleanedProjects++
			}
		}

		// Clean up Post table
		const postsToClean = await prisma.post.findMany({
			where: {
				featuredImage: { contains: 'cloudinary.com' }
			}
		})

		for (const post of postsToClean) {
			let updated = false
			const updates: any = {}

			if (post.featuredImage?.includes('cloudinary.com')) {
				const publicId = extractPublicId(post.featuredImage)
				if (publicId && publicIds.includes(publicId)) {
					updates.featuredImage = null
					updated = true
				}
			}

			// Handle attachments
			if (post.attachments && typeof post.attachments === 'object') {
				const attachments = post.attachments as any[]
				const cleanedAttachments = attachments.filter((attachment) => {
					if (attachment.url?.includes('cloudinary.com')) {
						const publicId = extractPublicId(attachment.url)
						return !(publicId && publicIds.includes(publicId))
					}
					return true
				})

				if (cleanedAttachments.length !== attachments.length) {
					updates.attachments = cleanedAttachments
					updated = true
				}
			}

			if (updated) {
				await prisma.post.update({
					where: { id: post.id },
					data: updates
				})
				results.cleanedPosts++
			}
		}
	} catch (error) {
		results.errors.push(error instanceof Error ? error.message : 'Unknown error')
		console.error('Error cleaning up broken references:', error)
	}

	return results
}
