import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import { requireAuth } from "../../middleware/auth";
import { success, error } from "../../utils/response";
import bcrypt from "bcryptjs";
import { config } from "../../config/env";
import { logAudit } from "../../services/audit";
import { parseBody } from "../../schemas/common";
import { UpdateProfileSchema } from "../../schemas/profile.schema";

export default async function profileRoutes(
  fastify: FastifyInstance,
): Promise<void> {
  fastify.get("/", { preHandler: requireAuth }, async (request, reply) => {
    const user = await prisma.users.findUnique({
      where: { id: request.authData!.userId },
      select: {
        id: true,
        username: true,
        email: true,
        first_name: true,
        last_name: true,
        totp_enabled: true,
        last_login: true,
        password_changed_at: true,
        roles: { select: { id: true, name: true, display_name: true } },
      },
    });
    if (!user) return error(reply, "Uživatel nenalezen", 404);
    return success(reply, user);
  });

  fastify.put("/", { preHandler: requireAuth }, async (request, reply) => {
    const parsed = parseBody(UpdateProfileSchema, request.body);
    if ("error" in parsed) return error(reply, parsed.error, 400);
    const body = parsed.data;
    const userId = request.authData!.userId;

    if (body.new_password && !body.current_password) {
      return error(reply, "Pro změnu hesla zadejte aktuální heslo", 400);
    }
    if (body.current_password && !body.new_password) {
      return error(reply, "Pro změnu hesla zadejte nové heslo", 400);
    }

    const data: Record<string, unknown> = {};
    if (body.email) {
      data.email = String(body.email).trim();
    }
    if (body.first_name) data.first_name = String(body.first_name);
    if (body.last_name) data.last_name = String(body.last_name);

    if (body.current_password && body.new_password) {
      const user = await prisma.users.findUnique({ where: { id: userId } });
      if (!user) return error(reply, "Uživatel nenalezen", 404);

      const valid = await bcrypt.compare(
        String(body.current_password),
        user.password_hash,
      );
      if (!valid) return error(reply, "Nesprávné aktuální heslo", 400);

      data.password_hash = await bcrypt.hash(
        String(body.new_password),
        config.security.bcryptCost,
      );
      data.password_changed_at = new Date();
    }

    // Wrap email uniqueness check and update in a transaction to prevent race condition
    try {
      await prisma.$transaction(async (tx) => {
        if (data.email) {
          const existing = await tx.users.findFirst({
            where: { email: String(data.email), id: { not: userId } },
          });
          if (existing) throw new Error("EMAIL_EXISTS");
        }
        await tx.users.update({ where: { id: userId }, data });
      });
    } catch (e) {
      if (e instanceof Error && e.message === "EMAIL_EXISTS") {
        return error(reply, "E-mail již existuje", 409);
      }
      throw e;
    }

    await logAudit({
      request,
      authData: request.authData,
      action: "update",
      entityType: "user",
      entityId: userId,
      description: data.password_hash ? "Změna hesla" : "Aktualizace profilu",
    });

    if (body.current_password && body.new_password) {
      await prisma.refresh_tokens.updateMany({
        where: { user_id: userId, replaced_at: null },
        data: { replaced_at: new Date() },
      });
    }

    return success(reply, null, 200, "Profil aktualizován");
  });
}
