import { TRPCError } from "@trpc/server";
import { t } from "@/lib/trpc";
import {
	CreatePlaybackSessionInput,
	CreatePlaybackSessionOutput,
	GetPlaybackSessionInput,
	ListPlaybackSessionsOutput,
	PlaybackSessionStatusOutput,
	SeekPlaybackSessionInput,
	SeekPlaybackSessionOutput,
	StopPlaybackSessionInput,
	StopPlaybackSessionOutput,
} from "@/models/trpc/playback";
import type { PlaybackService } from "@/services/playback";
import { authenticationMiddleware } from "@/trpc/middleware/authentication";

const playbackProcedure = t.procedure.use(authenticationMiddleware);

export const playbackRouter = t.router({
	createSession: playbackProcedure
		.input(CreatePlaybackSessionInput)
		.output(CreatePlaybackSessionOutput)
		.mutation(async ({ input, ctx }) => {
			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container not available",
				});
			}

			const playbackService =
				ctx.serviceContainer.getByName<PlaybackService>("PlaybackService");

			try {
				const result = await playbackService.createSession(input);

				return {
					sessionId: result.sessionId,
					playlistUrl: result.playlistUrl,
					status: result.status,
				};
			} catch (error) {
				if (error instanceof TRPCError) {
					throw error;
				}

				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Failed to create playback session",
					cause: error,
				});
			}
		}),

	getSessionStatus: playbackProcedure
		.input(GetPlaybackSessionInput)
		.output(PlaybackSessionStatusOutput)
		.query(async ({ input, ctx }) => {
			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container not available",
				});
			}

			const playbackService =
				ctx.serviceContainer.getByName<PlaybackService>("PlaybackService");

			const session = playbackService.getSession(input.sessionId);

			if (!session) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Playback session not found",
				});
			}

			return {
				sessionId: input.sessionId,
				status: session.status,
				startedAt: session.startedAt,
				lastAccessedAt: session.lastAccessedAt,
				isTranscoding: session.isTranscoding(),
				currentOffset: session.currentOffset,
				isRestarting: session.isRestarting,
			};
		}),

	stopSession: playbackProcedure
		.input(StopPlaybackSessionInput)
		.output(StopPlaybackSessionOutput)
		.mutation(async ({ input, ctx }) => {
			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container not available",
				});
			}

			const playbackService =
				ctx.serviceContainer.getByName<PlaybackService>("PlaybackService");

			const stopped = await playbackService.stopSession(input.sessionId);

			if (!stopped) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Playback session not found",
				});
			}

			return {
				success: true,
				message: "Playback session stopped successfully",
			};
		}),

	listSessions: playbackProcedure
		.output(ListPlaybackSessionsOutput)
		.query(async ({ ctx }) => {
			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container not available",
				});
			}

			const playbackService =
				ctx.serviceContainer.getByName<PlaybackService>("PlaybackService");

			const sessions = playbackService.listSessions();

			return sessions.map((session) => ({
				sessionId: session.sessionId,
				status: session.status,
				startedAt: session.startedAt,
				lastAccessedAt: session.lastAccessedAt,
				isTranscoding: session.isTranscoding(),
				currentOffset: session.currentOffset,
				isRestarting: session.isRestarting,
			}));
		}),

	seekSession: playbackProcedure
		.input(SeekPlaybackSessionInput)
		.output(SeekPlaybackSessionOutput)
		.mutation(async ({ input, ctx }) => {
			if (!ctx.serviceContainer) {
				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Service container not available",
				});
			}

			const playbackService =
				ctx.serviceContainer.getByName<PlaybackService>("PlaybackService");

			try {
				const result = await playbackService.seekSession(
					input.sessionId,
					input.seekTime,
				);

				return result;
			} catch (error) {
				if (error instanceof TRPCError) {
					throw error;
				}

				throw new TRPCError({
					code: "INTERNAL_SERVER_ERROR",
					message: "Failed to seek playback session",
					cause: error,
				});
			}
		}),
});
