"use server";

import { randomBytes } from "node:crypto";
import { revalidatePath } from "next/cache";
import { prisma } from "@/lib/prisma";
import { requireSession } from "@/lib/auth/session";
import { createNotifications } from "@/lib/notifications";
import type { ProjectStatus, Priority } from "@prisma/client";

const STATUSES: ProjectStatus[] = ["PLANNING", "ACTIVE", "ON_HOLD", "COMPLETED", "ARCHIVED"];
const PRIORITIES: Priority[] = ["LOW", "MEDIUM", "HIGH", "URGENT"];

async function guard(): Promise<string | null> {
  const session = await requireSession();
  return session.user.permissions?.includes("projects.manage") ? session.user.id : null;
}

function safeColor(v: string | null | undefined): string | null {
  if (!v) return null;
  return /^#[0-9a-fA-F]{3,8}$/.test(v) ? v.slice(0, 16) : null;
}

function parseDate(v: string | null | undefined): Date | null {
  if (!v) return null;
  const d = new Date(v);
  return Number.isNaN(d.getTime()) ? null : d;
}

function cleanKey(raw: string): string {
  return raw.toUpperCase().normalize("NFKD").replace(/[^A-Z0-9]/g, "").slice(0, 20);
}

async function uniqueKey(name: string): Promise<string> {
  const base = cleanKey(name).slice(0, 6) || "PRJ";
  let key = base;
  for (let i = 0; i < 5; i++) {
    const exists = await prisma.project.findUnique({ where: { key }, select: { key: true } });
    if (!exists) return key;
    key = `${base}${randomBytes(1).toString("hex").toUpperCase()}`.slice(0, 20);
  }
  return `${base}${Date.now().toString(36).toUpperCase()}`.slice(0, 20);
}

/** Prüft/normalisiert einen manuell gesetzten Projekt-Key (eindeutig). */
async function resolveKey(rawKey: string | undefined, name: string, selfId?: string): Promise<{ key?: string; error?: string }> {
  const wanted = rawKey ? cleanKey(rawKey) : "";
  if (!wanted) return { key: await uniqueKey(name) };
  if (wanted.length < 2) return { error: "Kürzel braucht mindestens 2 Zeichen." };
  const clash = await prisma.project.findFirst({
    where: { key: wanted, ...(selfId ? { id: { not: selfId } } : {}) },
    select: { id: true },
  });
  if (clash) return { error: "Kürzel ist bereits vergeben." };
  return { key: wanted };
}

async function validUserIds(ids: string[] | undefined): Promise<string[]> {
  if (!ids?.length) return [];
  const rows = await prisma.user.findMany({ where: { id: { in: ids } }, select: { id: true } });
  return rows.map((r) => r.id);
}

export type ProjectInput = {
  name: string;
  key?: string;
  description?: string;
  status?: ProjectStatus;
  priority?: Priority;
  progress?: number;
  color?: string | null;
  startDate?: string | null;
  deadline?: string | null;
  leadId?: string | null;
  memberIds?: string[];
};

export type ProjectResult = { id?: string; error?: string };

function normalize(input: ProjectInput) {
  return {
    status: STATUSES.includes(input.status as ProjectStatus) ? (input.status as ProjectStatus) : "PLANNING",
    priority: PRIORITIES.includes(input.priority as Priority) ? (input.priority as Priority) : "MEDIUM",
    progress: Math.max(0, Math.min(100, Math.round(input.progress ?? 0))),
    color: safeColor(input.color),
    startDate: parseDate(input.startDate),
    deadline: parseDate(input.deadline),
  };
}

export async function createProjectAction(input: ProjectInput): Promise<ProjectResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  const name = input.name.trim();
  if (!name) return { error: "Name erforderlich." };

  const n = normalize(input);
  const keyRes = await resolveKey(input.key, name);
  if (keyRes.error) return { error: keyRes.error };
  const leadId = input.leadId ? (await validUserIds([input.leadId]))[0] ?? null : null;
  const memberIds = await validUserIds(input.memberIds);

  const project = await prisma.project.create({
    data: {
      key: keyRes.key!,
      name: name.slice(0, 160),
      description: input.description?.trim() || null,
      status: n.status as ProjectStatus,
      priority: n.priority as Priority,
      progress: n.progress,
      color: n.color,
      startDate: n.startDate,
      deadline: n.deadline,
      leadId,
      members: { create: memberIds.map((userId) => ({ userId })) },
    },
    select: { id: true },
  });

  const notify = [...new Set([...(leadId ? [leadId] : []), ...memberIds])].filter((id) => id !== actorId);
  if (notify.length) {
    await createNotifications(notify, {
      type: "GENERIC",
      title: "Zu Projekt hinzugefügt",
      body: name,
      link: "/projects",
    });
  }

  revalidatePath("/projects");
  return { id: project.id };
}

export async function updateProjectAction(projectId: string, input: ProjectInput): Promise<ProjectResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  const existing = await prisma.project.findUnique({ where: { id: projectId }, select: { id: true } });
  if (!existing) return { error: "Projekt nicht gefunden." };

  const name = input.name.trim();
  if (!name) return { error: "Name erforderlich." };
  const n = normalize(input);
  const keyRes = await resolveKey(input.key, name, projectId);
  if (keyRes.error) return { error: keyRes.error };
  const leadId = input.leadId ? (await validUserIds([input.leadId]))[0] ?? null : null;
  const memberIds = input.memberIds !== undefined ? await validUserIds(input.memberIds) : null;

  await prisma.$transaction(async (tx) => {
    await tx.project.update({
      where: { id: projectId },
      data: {
        key: keyRes.key!,
        name: name.slice(0, 160),
        description: input.description?.trim() || null,
        status: n.status as ProjectStatus,
        priority: n.priority as Priority,
        progress: n.progress,
        color: n.color,
        startDate: n.startDate,
        deadline: n.deadline,
        leadId,
      },
    });
    if (memberIds !== null) {
      await tx.projectMember.deleteMany({ where: { projectId } });
      if (memberIds.length) {
        await tx.projectMember.createMany({ data: memberIds.map((userId) => ({ projectId, userId })) });
      }
    }
  });

  revalidatePath("/projects");
  return { id: projectId };
}

export async function deleteProjectAction(projectId: string): Promise<ProjectResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  await prisma.project.delete({ where: { id: projectId } });
  revalidatePath("/projects");
  return { id: projectId };
}

// ── Meilensteine ──────────────────────────────────────────────────────────
export type MilestoneResult = { id?: string; error?: string };

export async function addMilestoneAction(
  projectId: string,
  title: string,
  dueDate?: string | null,
): Promise<MilestoneResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  const t = title.trim();
  if (!t) return { error: "Titel erforderlich." };
  const position = await prisma.milestone.count({ where: { projectId } });
  const m = await prisma.milestone.create({
    data: { projectId, title: t.slice(0, 160), dueDate: parseDate(dueDate), position },
    select: { id: true },
  });
  revalidatePath("/projects");
  return { id: m.id };
}

export async function toggleMilestoneAction(milestoneId: string, done: boolean): Promise<MilestoneResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  await prisma.milestone.update({ where: { id: milestoneId }, data: { done } });
  revalidatePath("/projects");
  return { id: milestoneId };
}

export async function deleteMilestoneAction(milestoneId: string): Promise<MilestoneResult> {
  const actorId = await guard();
  if (!actorId) return { error: "Keine Berechtigung." };
  await prisma.milestone.deleteMany({ where: { id: milestoneId } });
  revalidatePath("/projects");
  return { id: milestoneId };
}
