import { db } from "@cap/database";
import { getCurrentUser } from "@cap/database/auth/session";
import {
	comments,
	organizationMembers,
	organizations,
	sharedVideos,
	spaces,
	spaceVideos,
	users,
	videos,
} from "@cap/database/schema";
import type { VideoMetadata } from "@cap/database/types";
import { buildEnv } from "@cap/env";
import { Logo } from "@cap/ui";
import { eq, type InferSelectModel, sql } from "drizzle-orm";
import type { Metadata } from "next";
import { headers } from "next/headers";
import Link from "next/link";
import { notFound } from "next/navigation";
import { generateAiMetadata } from "@/actions/videos/generate-ai-metadata";
import { getVideoAnalytics } from "@/actions/videos/get-analytics";
import { getDashboardData } from "@/app/(org)/dashboard/dashboard-data";
import { createNotification } from "@/lib/Notification";
import { transcribeVideo } from "@/lib/transcribe";
import { userHasAccessToVideo } from "@/utils/auth";
import { isAiGenerationEnabled } from "@/utils/flags";
import { PasswordOverlay } from "./_components/PasswordOverlay";
import { ShareHeader } from "./_components/ShareHeader";
import { Share } from "./Share";

export const dynamic = "auto";
export const dynamicParams = true;
export const revalidate = 30;

// Helper function to fetch shared spaces data for a video
async function getSharedSpacesForVideo(videoId: string) {
	// Fetch space-level sharing
	const spaceSharing = await db()
		.select({
			id: spaces.id,
			name: spaces.name,
			organizationId: spaces.organizationId,
			iconUrl: organizations.iconUrl,
		})
		.from(spaceVideos)
		.innerJoin(spaces, eq(spaceVideos.spaceId, spaces.id))
		.innerJoin(organizations, eq(spaces.organizationId, organizations.id))
		.where(eq(spaceVideos.videoId, videoId));

	// Fetch organization-level sharing
	const orgSharing = await db()
		.select({
			id: organizations.id,
			name: organizations.name,
			organizationId: organizations.id,
			iconUrl: organizations.iconUrl,
		})
		.from(sharedVideos)
		.innerJoin(organizations, eq(sharedVideos.organizationId, organizations.id))
		.where(eq(sharedVideos.videoId, videoId));

	const sharedSpaces: Array<{
		id: string;
		name: string;
		organizationId: string;
		iconUrl?: string;
	}> = [];

	// Add space-level sharing
	spaceSharing.forEach((space) => {
		sharedSpaces.push({
			id: space.id,
			name: space.name,
			organizationId: space.organizationId,
			iconUrl: space.iconUrl || undefined,
		});
	});

	// Add organization-level sharing
	orgSharing.forEach((org) => {
		sharedSpaces.push({
			id: org.id,
			name: org.name,
			organizationId: org.organizationId,
			iconUrl: org.iconUrl || undefined,
		});
	});

	return sharedSpaces;
}

type Props = {
	params: { [key: string]: string | string[] | undefined };
	searchParams: { [key: string]: string | string[] | undefined };
};

type CommentWithAuthor = typeof comments.$inferSelect & {
	authorName: string | null;
};

type VideoWithOrganization = typeof videos.$inferSelect & {
	sharedOrganization?: {
		organizationId: string;
	} | null;
	organizationMembers?: string[];
	organizationId?: string;
	sharedOrganizations?: { id: string; name: string }[];
	password?: string | null;
	hasPassword?: boolean;
};

export async function generateMetadata({ params }: Props): Promise<Metadata> {
	const videoId = params.videoId as string;

	console.log(
		"[generateMetadata] Fetching video metadata for videoId:",
		videoId,
	);
	const query = await db().select().from(videos).where(eq(videos.id, videoId));

	if (query.length === 0) {
		console.log("[generateMetadata] No video found for videoId:", videoId);
		return notFound();
	}

	const video = query[0];

	if (!video) {
		return notFound();
	}

	const userPromise = getCurrentUser();
	const userAccess = await userHasAccessToVideo(userPromise, video);

	const headersList = headers();
	const referrer = headersList.get("x-referrer") || "";

	const allowedReferrers = [
		"x.com",
		"twitter.com",
		"facebook.com",
		"fb.com",
		"slack.com",
		"notion.so",
		"linkedin.com",
	];

	const isAllowedReferrer = allowedReferrers.some((domain) =>
		referrer.includes(domain),
	);

	const robotsDirective = isAllowedReferrer
		? "index, follow"
		: "noindex, nofollow";

	if (video.public === false && userAccess !== "has-access") {
		return {
			title: "Cap: This video is private",
			description: "This video is private and cannot be shared.",
			openGraph: {
				images: [
					{
						url: new URL(
							`/api/video/og?videoId=${videoId}`,
							buildEnv.NEXT_PUBLIC_WEB_URL,
						).toString(),
						width: 1200,
						height: 630,
					},
				],
				videos: [
					{
						url: new URL(
							`/api/playlist?userId=${video.ownerId}&videoId=${video.id}`,
							buildEnv.NEXT_PUBLIC_WEB_URL,
						).toString(),
						width: 1280,
						height: 720,
						type: "video/mp4",
					},
				],
			},
			robots: "noindex, nofollow",
		};
	}

	if (video.password !== null && userAccess !== "has-access") {
		return {
			title: "Cap: Password Protected Video",
			description: "This video is password protected.",
			openGraph: {
				images: [
					{
						url: new URL(
							`/api/video/og?videoId=${videoId}`,
							buildEnv.NEXT_PUBLIC_WEB_URL,
						).toString(),
						width: 1200,
						height: 630,
					},
				],
			},
			twitter: {
				card: "summary_large_image",
				title: "Cap: Password Protected Video",
				description: "This video is password protected.",
				images: [
					new URL(
						`/api/video/og?videoId=${videoId}`,
						buildEnv.NEXT_PUBLIC_WEB_URL,
					).toString(),
				],
			},
			robots: "noindex, nofollow",
		};
	}

	return {
		title: video.name + " | Cap Recording",
		description: "Watch this video on Cap",
		openGraph: {
			images: [
				{
					url: new URL(
						`/api/video/og?videoId=${videoId}`,
						buildEnv.NEXT_PUBLIC_WEB_URL,
					).toString(),
					width: 1200,
					height: 630,
				},
			],
			videos: [
				{
					url: new URL(
						`/api/playlist?userId=${video.ownerId}&videoId=${video.id}`,
						buildEnv.NEXT_PUBLIC_WEB_URL,
					).toString(),
					width: 1280,
					height: 720,
					type: "video/mp4",
				},
			],
		},
		twitter: {
			card: "player",
			title: video.name + " | Cap Recording",
			description: "Watch this video on Cap",
			images: [
				new URL(
					`/api/video/og?videoId=${videoId}`,
					buildEnv.NEXT_PUBLIC_WEB_URL,
				).toString(),
			],
			players: {
				playerUrl: new URL(
					`/s/${videoId}`,
					buildEnv.NEXT_PUBLIC_WEB_URL,
				).toString(),
				streamUrl: new URL(
					`/api/playlist?userId=${video.ownerId}&videoId=${video.id}`,
					buildEnv.NEXT_PUBLIC_WEB_URL,
				).toString(),
				width: 1280,
				height: 720,
			},
		},
		robots: robotsDirective,
	};
}

export default async function ShareVideoPage(props: Props) {
	const params = props.params;
	const searchParams = props.searchParams;
	const videoId = params.videoId as string;

	const user = await getCurrentUser();

	const [video] = await db()
		.select({
			id: videos.id,
			name: videos.name,
			ownerId: videos.ownerId,
			createdAt: videos.createdAt,
			updatedAt: videos.updatedAt,
			awsRegion: videos.awsRegion,
			awsBucket: videos.awsBucket,
			bucket: videos.bucket,
			metadata: videos.metadata,
			public: videos.public,
			videoStartTime: videos.videoStartTime,
			audioStartTime: videos.audioStartTime,
			xStreamInfo: videos.xStreamInfo,
			jobId: videos.jobId,
			jobStatus: videos.jobStatus,
			isScreenshot: videos.isScreenshot,
			skipProcessing: videos.skipProcessing,
			transcriptionStatus: videos.transcriptionStatus,
			password: videos.password,
			source: videos.source,
			sharedOrganization: {
				organizationId: sharedVideos.organizationId,
			},
		})
		.from(videos)
		.leftJoin(sharedVideos, eq(videos.id, sharedVideos.videoId))
		.where(eq(videos.id, videoId));

	if (user && video && user.id !== video.ownerId) {
		try {
			await createNotification({ type: "view", videoId, authorId: user.id });
		} catch (error) {
			console.error("Failed to create view notification:", error);
		}
	}

	if (!video) {
		console.log("[ShareVideoPage] No video found for videoId:", videoId);
		return <p>No video found</p>;
	}

	const userAccess = await userHasAccessToVideo(user, video);

	if (userAccess === "private") {
		return (
			<div className="flex flex-col justify-center items-center p-4 min-h-screen text-center">
				<h1 className="mb-4 text-2xl font-bold">This video is private</h1>
				<p className="text-gray-400">
					If you own this video, please <Link href="/login">sign in</Link> to
					manage sharing.
				</p>
			</div>
		);
	}

	return (
		<div className="min-h-screen flex flex-col bg-[#F7F8FA]">
			<PasswordOverlay
				isOpen={userAccess === "needs-password"}
				videoId={video.id}
			/>
			{userAccess === "has-access" && (
				<AuthorizedContent
					video={video}
					user={user}
					searchParams={searchParams}
				/>
			)}
		</div>
	);
}

async function AuthorizedContent({
	video,
	user: _user,
	searchParams,
}: {
	video: Omit<InferSelectModel<typeof videos>, "folderId"> & {
		sharedOrganization: { organizationId: string } | null;
	};
	user: MaybePromise<InferSelectModel<typeof users> | null>;
	searchParams: { [key: string]: string | string[] | undefined };
}) {
	const user = await _user;
	const videoId = video.id;
	const userId = user?.id;
	const commentId = searchParams.comment as string | undefined;
	const replyId = searchParams.reply as string | undefined;

	// Fetch spaces data for the sharing dialog
	let spacesData = null;
	if (user) {
		try {
			const dashboardData = await getDashboardData(user);
			spacesData = dashboardData.spacesData;
		} catch (error) {
			console.error("Failed to fetch spaces data for sharing dialog:", error);
			spacesData = [];
		}
	}

	// Fetch shared spaces data for this video
	const sharedSpaces = await getSharedSpacesForVideo(videoId);

	let aiGenerationEnabled = false;
	const videoOwnerQuery = await db()
		.select({
			email: users.email,
			stripeSubscriptionStatus: users.stripeSubscriptionStatus,
		})
		.from(users)
		.where(eq(users.id, video.ownerId))
		.limit(1);

	if (videoOwnerQuery.length > 0 && videoOwnerQuery[0]) {
		const videoOwner = videoOwnerQuery[0];
		aiGenerationEnabled = await isAiGenerationEnabled(videoOwner);
	}

	if (video.sharedOrganization?.organizationId) {
		const organization = await db()
			.select()
			.from(organizations)
			.where(eq(organizations.id, video.sharedOrganization.organizationId))
			.limit(1);

		if (organization[0]?.allowedEmailDomain) {
			if (
				!user?.email ||
				!user.email.endsWith(`@${organization[0].allowedEmailDomain}`)
			) {
				console.log(
					"[ShareVideoPage] Access denied - domain restriction:",
					organization[0].allowedEmailDomain,
				);
				return (
					<div className="flex flex-col justify-center items-center p-4 min-h-screen text-center">
						<h1 className="mb-4 text-2xl font-bold">Access Restricted</h1>
						<p className="mb-2 text-gray-10">
							This video is only accessible to members of this organization.
						</p>
						<p className="text-gray-600">
							Please sign in with your organization email address to access this
							content.
						</p>
					</div>
				);
			}
		}
	}

	if (
		video.transcriptionStatus !== "COMPLETE" &&
		video.transcriptionStatus !== "PROCESSING"
	) {
		console.log("[ShareVideoPage] Starting transcription for video:", videoId);
		await transcribeVideo(videoId, video.ownerId, aiGenerationEnabled);

		const updatedVideoQuery = await db()
			.select({
				id: videos.id,
				name: videos.name,
				ownerId: videos.ownerId,
				createdAt: videos.createdAt,
				updatedAt: videos.updatedAt,
				awsRegion: videos.awsRegion,
				awsBucket: videos.awsBucket,
				bucket: videos.bucket,
				metadata: videos.metadata,
				public: videos.public,
				videoStartTime: videos.videoStartTime,
				audioStartTime: videos.audioStartTime,
				xStreamInfo: videos.xStreamInfo,
				jobId: videos.jobId,
				jobStatus: videos.jobStatus,
				isScreenshot: videos.isScreenshot,
				skipProcessing: videos.skipProcessing,
				transcriptionStatus: videos.transcriptionStatus,
				source: videos.source,
				sharedOrganization: {
					organizationId: sharedVideos.organizationId,
				},
			})
			.from(videos)
			.leftJoin(sharedVideos, eq(videos.id, sharedVideos.videoId))
			.where(eq(videos.id, videoId))
			.execute();

		if (updatedVideoQuery[0]) {
			Object.assign(video, updatedVideoQuery[0]);
			console.log(
				"[ShareVideoPage] Updated transcription status:",
				video.transcriptionStatus,
			);
		}
	}

	const currentMetadata = (video.metadata as VideoMetadata) || {};
	const metadata = currentMetadata;
	let initialAiData = null;

	if (metadata.summary || metadata.chapters || metadata.aiTitle) {
		initialAiData = {
			title: metadata.aiTitle || null,
			summary: metadata.summary || null,
			chapters: metadata.chapters || null,
			processing: metadata.aiProcessing || false,
		};
	} else if (metadata.aiProcessing) {
		initialAiData = {
			title: null,
			summary: null,
			chapters: null,
			processing: true,
		};
	}

	if (
		video.transcriptionStatus === "COMPLETE" &&
		!currentMetadata.aiProcessing &&
		!currentMetadata.summary &&
		!currentMetadata.chapters &&
		!currentMetadata.generationError &&
		aiGenerationEnabled
	) {
		try {
			generateAiMetadata(videoId, video.ownerId).catch((error) => {
				console.error(
					`[ShareVideoPage] Error generating AI metadata for video ${videoId}:`,
					error,
				);
			});
		} catch (error) {
			console.error(
				`[ShareVideoPage] Error starting AI metadata generation for video ${videoId}:`,
				error,
			);
		}
	}

	const customDomainPromise = (async () => {
		if (!user) {
			return { customDomain: null, domainVerified: false };
		}
		const activeOrganizationId = user.activeOrganizationId;
		if (!activeOrganizationId) {
			return { customDomain: null, domainVerified: false };
		}

		// Fetch the active org
		const orgArr = await db()
			.select({
				customDomain: organizations.customDomain,
				domainVerified: organizations.domainVerified,
			})
			.from(organizations)
			.where(eq(organizations.id, activeOrganizationId))
			.limit(1);

		const org = orgArr[0];
		if (
			org &&
			org.customDomain &&
			org.domainVerified !== null &&
			user.id === video.ownerId
		) {
			return { customDomain: org.customDomain, domainVerified: true };
		}
		return { customDomain: null, domainVerified: false };
	})();

	const sharedOrganizationsPromise = db()
		.select({ id: sharedVideos.organizationId, name: organizations.name })
		.from(sharedVideos)
		.innerJoin(organizations, eq(sharedVideos.organizationId, organizations.id))
		.where(eq(sharedVideos.videoId, videoId));

	const userOrganizationsPromise = (async () => {
		if (!userId) return [];

		const [ownedOrganizations, memberOrganizations] = await Promise.all([
			db()
				.select({ id: organizations.id, name: organizations.name })
				.from(organizations)
				.where(eq(organizations.ownerId, userId)),
			db()
				.select({ id: organizations.id, name: organizations.name })
				.from(organizations)
				.innerJoin(
					organizationMembers,
					eq(organizations.id, organizationMembers.organizationId),
				)
				.where(eq(organizationMembers.userId, userId)),
		]);

		const allOrganizations = [...ownedOrganizations, ...memberOrganizations];
		const uniqueOrganizationIds = new Set();

		return allOrganizations.filter((organization) => {
			if (uniqueOrganizationIds.has(organization.id)) return false;
			uniqueOrganizationIds.add(organization.id);
			return true;
		});
	})();

	const membersListPromise = video.sharedOrganization?.organizationId
		? db()
				.select({ userId: organizationMembers.userId })
				.from(organizationMembers)
				.where(
					eq(
						organizationMembers.organizationId,
						video.sharedOrganization.organizationId,
					),
				)
		: Promise.resolve([]);

	const commentsPromise = (async () => {
		let toplLevelCommentId: string | undefined;

		if (replyId) {
			const [parentComment] = await db()
				.select({ parentCommentId: comments.parentCommentId })
				.from(comments)
				.where(eq(comments.id, replyId))
				.limit(1);
			toplLevelCommentId = parentComment?.parentCommentId;
		}

		const commentToBringToTheTop = toplLevelCommentId ?? commentId;

		const allComments = await db()
			.select({
				id: comments.id,
				content: comments.content,
				timestamp: comments.timestamp,
				type: comments.type,
				authorId: comments.authorId,
				videoId: comments.videoId,
				createdAt: comments.createdAt,
				updatedAt: comments.updatedAt,
				parentCommentId: comments.parentCommentId,
				authorName: users.name,
			})
			.from(comments)
			.leftJoin(users, eq(comments.authorId, users.id))
			.where(eq(comments.videoId, videoId))
			.orderBy(
				commentToBringToTheTop
					? sql`CASE WHEN ${comments.id} = ${commentToBringToTheTop} THEN 0 ELSE 1 END, ${comments.createdAt}`
					: comments.createdAt,
			);

		return allComments;
	})();

	const viewsPromise = getVideoAnalytics(videoId).then((v) => v.count);

	const [
		membersList,
		userOrganizations,
		sharedOrganizations,
		{ customDomain, domainVerified },
	] = await Promise.all([
		membersListPromise,
		userOrganizationsPromise,
		sharedOrganizationsPromise,
		customDomainPromise,
	]);

	const videoWithOrganizationInfo: VideoWithOrganization = {
		...video,
		organizationMembers: membersList.map((member) => member.userId),
		organizationId: video.sharedOrganization?.organizationId ?? undefined,
		sharedOrganizations: sharedOrganizations,
		password: null,
		hasPassword: video.password !== null,
		folderId: null,
	};

	return (
		<>
			<div className="container flex-1 px-4 py-4 mx-auto">
				<ShareHeader
					data={{
						...videoWithOrganizationInfo,
						createdAt: video.metadata?.customCreatedAt
							? new Date(video.metadata.customCreatedAt)
							: video.createdAt,
					}}
					user={user}
					customDomain={customDomain}
					domainVerified={domainVerified}
					sharedOrganizations={
						videoWithOrganizationInfo.sharedOrganizations || []
					}
					sharedSpaces={sharedSpaces}
					userOrganizations={userOrganizations}
					spacesData={spacesData}
					NODE_ENV={process.env.NODE_ENV}
				/>

				<Share
					data={videoWithOrganizationInfo}
					user={user}
					comments={commentsPromise}
					views={viewsPromise}
					customDomain={customDomain}
					domainVerified={domainVerified}
					userOrganizations={userOrganizations}
					initialAiData={initialAiData}
					aiGenerationEnabled={aiGenerationEnabled}
				/>
			</div>
			<div className="py-4 mt-auto">
				<a
					target="_blank"
					href={`/?ref=video_${video.id}`}
					className="flex justify-center items-center px-4 py-2 mx-auto space-x-2 rounded-full bg-gray-1 new-card-style w-fit"
				>
					<span className="text-sm">Recorded with</span>
					<Logo className="w-14 h-auto" />
				</a>
			</div>
		</>
	);
}
