import { ExperienceFindingPriority, ExperienceFindingStatus } from "@prisma/client";
import { NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import {
  canManageExperienciometro,
  ensureExperienceBaseline,
  mapExperienceFinding,
  recalculateLocationMetrics,
} from "@/lib/experienciometro";
import type { ExperienceFindingPatchPayload } from "@/lib/experienciometro/types";

function parseDate(value: string | null | undefined): Date | null {
  if (!value) {
    return null;
  }

  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) {
    return null;
  }

  return parsed;
}

function toPriority(value: string | undefined): ExperienceFindingPriority | null {
  if (value === "low" || value === "medium" || value === "high" || value === "critical") {
    return value;
  }

  return null;
}

function toStatus(value: string | undefined): ExperienceFindingStatus | null {
  if (value === "open" || value === "in_progress" || value === "resolved" || value === "closed") {
    return value;
  }

  return null;
}

export async function PATCH(request: Request, context: { params: { id: string } }) {
  const session = await getServerSession(authOptions);
  if (!session?.user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  if (!canManageExperienciometro(session.user.role)) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }

  await ensureExperienceBaseline(prisma, session.user.id);

  const findingId = context.params.id;
  const existing = await prisma.experienceFinding.findUnique({
    where: { id: findingId },
    select: {
      id: true,
      locationId: true,
      status: true,
    },
  });

  if (!existing) {
    return NextResponse.json({ error: "Finding not found." }, { status: 404 });
  }

  const body = (await request.json()) as ExperienceFindingPatchPayload;

  const priority = body.priority ? toPriority(body.priority) : null;
  if (body.priority && !priority) {
    return NextResponse.json({ error: "Invalid priority." }, { status: 400 });
  }

  const status = body.status ? toStatus(body.status) : null;
  if (body.status && !status) {
    return NextResponse.json({ error: "Invalid status." }, { status: 400 });
  }

  const dueDate = body.dueDate === null ? null : body.dueDate ? parseDate(body.dueDate) : undefined;
  if (body.dueDate && !dueDate) {
    return NextResponse.json({ error: "Invalid dueDate." }, { status: 400 });
  }

  const responsibleUserId = body.responsibleUserId === null ? null : body.responsibleUserId?.trim() || undefined;
  if (responsibleUserId) {
    const user = await prisma.user.findUnique({
      where: { id: responsibleUserId },
      select: { id: true },
    });

    if (!user) {
      return NextResponse.json({ error: "Invalid responsibleUserId." }, { status: 400 });
    }
  }

  const nextStatus = status ?? existing.status;
  const isResolved = nextStatus === ExperienceFindingStatus.resolved || nextStatus === ExperienceFindingStatus.closed;

  const finding = await prisma.$transaction(async (tx) => {
    const updated = await tx.experienceFinding.update({
      where: { id: findingId },
      data: {
        ...(typeof body.title === "string" ? { title: body.title.trim() || "Hallazgo" } : {}),
        ...(typeof body.description === "string" ? { description: body.description.trim() } : {}),
        ...(priority ? { priority } : {}),
        ...(status ? { status } : {}),
        ...(dueDate === undefined ? {} : { dueDate }),
        ...(responsibleUserId === undefined ? {} : { responsibleUserId }),
        resolvedAt: isResolved ? new Date() : null,
      },
      include: {
        responsibleUser: {
          select: {
            name: true,
            email: true,
          },
        },
        createdBy: {
          select: {
            name: true,
            email: true,
          },
        },
      },
    });

    await recalculateLocationMetrics(tx, existing.locationId);
    return updated;
  });

  return NextResponse.json({
    ok: true,
    finding: mapExperienceFinding(finding),
  });
}
