import { Elysia, t } from "elysia";
import { authMiddleware } from "../middlewares/auth";
import { eq } from "drizzle-orm";
import db from "../db";
import { apps } from "../db/schema/apps";
import { serverSessionService } from "../services/serverSession";

export const sessionController = new Elysia({ prefix: "/sessions" })
  // PATCH /sessions/:id/status - обновить статус сессии (публичный endpoint для сессионного сервера)
  .patch(
    "/:id/status",
    async ({ params, body, status }) => {
      const { id } = params;
      const {
        status: sessionStatus,
        appPid,
        cirrusPid,
      } = body as {
        status?: "starting" | "started" | "ending" | "ended";
        appPid?: number;
        cirrusPid?: number;
      };

      // Проверить, что сессия существует
      const session = await serverSessionService.findById(id);

      if (!session) {
        return status(404, "Session not found");
      }

      // Обновить сессию
      const updatedSession = await serverSessionService.update(id, {
        status: sessionStatus,
        appPid,
        cirrusPid,
      });

      return { session: updatedSession };
    },
    {
      body: t.Object({
        status: t.Optional(
          t.Union([
            t.Literal("starting"),
            t.Literal("started"),
            t.Literal("ending"),
            t.Literal("ended"),
          ])
        ),
        appPid: t.Optional(t.Number()),
        cirrusPid: t.Optional(t.Number()),
      }),
    }
  )
  // POST /sessions/:id/assign-server - назначить сервер для сессии (публичный endpoint для сессионного сервера)
  .post(
    "/:id/assign-server",
    async ({ params, body, status }) => {
      const { id } = params;
      const { requiredGpuMb } = body as { requiredGpuMb?: number };

      try {
        const updatedSession = await serverSessionService.assignServer(
          id,
          requiredGpuMb
        );
        return { session: updatedSession };
      } catch (error) {
        if (error instanceof Error) {
          return status(400, error.message);
        }
        return status(500, "Failed to assign server");
      }
    },
    {
      body: t.Object({
        requiredGpuMb: t.Optional(t.Number({ minimum: 0 })),
      }),
    }
  )
  // Все роуты требуют авторизации
  .use(authMiddleware)
  // GET /sessions - получить список сессий пользователя
  .get("/", async ({ currentUser, query }) => {
    const { status, mode } = query as {
      status?: "starting" | "started" | "ending" | "ended";
      mode?: "stream" | "local";
    };

    const sessions = await serverSessionService.findByUserId(currentUser.id, {
      status,
      mode,
    });

    return { sessions };
  })
  // GET /sessions/:id - получить информацию о конкретной сессии
  .get("/:id", async ({ params, currentUser, status }) => {
    const { id } = params;

    const session = await serverSessionService.findByIdForUser(
      id,
      currentUser.id
    );

    if (!session) {
      return status(404, "Session not found");
    }

    return { session };
  })
  // POST /sessions - создать новую сессию
  .post(
    "/",
    async ({ body, currentUser, status }) => {
      const { appId, mode, serverId } = body as {
        appId: string;
        mode: "stream" | "local";
        serverId?: string;
      };

      // Проверить, что приложение существует
      const app = await db.query.apps.findFirst({
        where: eq(apps.id, appId),
      });

      if (!app) {
        return status(404, "App not found");
      }

      // Проверить, что пользователь не имеет активных сессий этого приложения
      const hasActive = await serverSessionService.hasActiveSession(
        currentUser.id,
        appId
      );

      if (hasActive) {
        return status(409, "User already has an active session for this app");
      }

      // Создать сессию
      try {
        const newSession = await serverSessionService.create({
          appId,
          userId: currentUser.id,
          mode,
          serverId,
        });

        return { session: newSession };
      } catch (error) {
        if (error instanceof Error) {
          return status(503, error.message);
        }
        return status(500, "Failed to create session");
      }
    },
    {
      body: t.Object({
        appId: t.String({ format: "uuid" }),
        mode: t.Union([t.Literal("stream"), t.Literal("local")]),
        serverId: t.Optional(t.String({ format: "uuid" })),
      }),
    }
  )
  // PATCH /sessions/:id - обновить статус сессии
  .patch(
    "/:id",
    async ({ params, body, currentUser, status }) => {
      const { id } = params;
      const {
        status: sessionStatus,
        appPid,
        cirrusPid,
        endAt,
      } = body as {
        status?: "starting" | "started" | "ending" | "ended";
        appPid?: number;
        cirrusPid?: number;
        endAt?: string;
      };

      // Проверить, что сессия существует и принадлежит пользователю
      const session = await serverSessionService.findByIdForUser(
        id,
        currentUser.id
      );

      if (!session) {
        return status(404, "Session not found");
      }

      // Обновить сессию
      const updatedSession = await serverSessionService.update(id, {
        status: sessionStatus,
        appPid,
        cirrusPid,
        endAt: endAt ? new Date(endAt) : undefined,
      });

      return { session: updatedSession };
    },
    {
      body: t.Object({
        status: t.Optional(
          t.Union([
            t.Literal("starting"),
            t.Literal("started"),
            t.Literal("ending"),
            t.Literal("ended"),
          ])
        ),
        appPid: t.Optional(t.Number()),
        cirrusPid: t.Optional(t.Number()),
        endAt: t.Optional(t.String({ format: "date-time" })),
      }),
    }
  )
  // DELETE /sessions/:id - удалить (завершить) сессию
  .delete("/:id", async ({ params, currentUser, status }) => {
    const { id } = params;

    // Проверить, что сессия существует и принадлежит пользователю
    const session = await serverSessionService.findByIdForUser(
      id,
      currentUser.id
    );

    if (!session) {
      return status(404, "Session not found");
    }

    // Если сессия активна, изменить статус на "ending"
    if (session.status === "started" || session.status === "starting") {
      await serverSessionService.end(id);
      return { message: "Session is ending" };
    }

    // Если сессия уже завершена или завершается
    return { message: "Session already ended or ending" };
  })
  // POST /sessions/:id/extend - продлить сессию
  .post(
    "/:id/extend",
    async ({ params, body, currentUser, status }) => {
      const { id } = params;
      const { minutes } = body as { minutes: number };

      // Проверить, что сессия существует и принадлежит пользователю
      const session = await serverSessionService.findByIdForUser(
        id,
        currentUser.id
      );

      if (!session) {
        return status(404, "Session not found");
      }

      // Проверить, что сессия активна
      if (session.status !== "started") {
        return status(400, "Can only extend active sessions");
      }

      // Продлить сессию
      try {
        const updatedSession = await serverSessionService.extend(id, minutes);
        return { session: updatedSession };
      } catch (error) {
        if (error instanceof Error) {
          return status(400, error.message);
        }
        return status(500, "Failed to extend session");
      }
    },
    {
      body: t.Object({
        minutes: t.Number({ minimum: 1, maximum: 120 }),
      }),
    }
  );
