"use server";

import { prisma } from "@/lib/prisma";
import { requireSession } from "@/lib/auth/session";
import { createNotifications } from "@/lib/notifications";
import { getTaskById, type TaskDTO, type TaskCommentDTO, type ColumnDTO } from "./queries";
import type { Priority } from "@prisma/client";

const PRIORITIES: Priority[] = ["LOW", "MEDIUM", "HIGH", "URGENT"];

const DEFAULT_COLUMNS = [
  { key: "ideas", name: "Ideen", color: "#8aa0c8" },
  { key: "planned", name: "Geplant", color: "#9ed7ff" },
  { key: "in_progress", name: "In Arbeit", color: "#38bdf8" },
  { key: "review", name: "Review", color: "#c8bcff" },
  { key: "approval", name: "Freigabe", color: "#ffc36b" },
  { key: "done", name: "Erledigt", color: "#7ee787" },
  { key: "archived", name: "Archiviert", color: "#6b7a99" },
];
const COLUMN_COLORS = ["#9ed7ff", "#c8bcff", "#ff9fd7", "#38bdf8", "#ffc36b", "#7ee787", "#8aa0c8"];

const MAX_COLUMNS = 5;

/** Validiert einen Hex-Farbwert (#rgb / #rrggbb), sonst 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;
}

/** „Benutzen": Aufgaben anlegen/verschieben/kommentieren (kanban.manage). */
async function requireManage(): Promise<{ ok: true; userId: string } | { ok: false }> {
  const session = await requireSession();
  if (!session.user.permissions?.includes("kanban.manage")) return { ok: false };
  return { ok: true, userId: session.user.id };
}

/** Board-Admin: darf Struktur ändern (Spalten, Labels, Farben, Einstellungen). */
async function requireBoardAdmin(
  boardId: string | undefined,
): Promise<{ ok: true; userId: string } | { ok: false }> {
  const session = await requireSession();
  const userId = session.user.id;
  const superAdmin = Boolean(session.user.permissions?.includes("admin.access"));
  if (superAdmin) return { ok: true, userId };
  if (!boardId) return { ok: false };
  const board = await prisma.board.findUnique({ where: { id: boardId }, select: { adminId: true } });
  if (board && board.adminId === userId) return { ok: true, userId };
  return { ok: false };
}

/** Board-Admin anhand einer Spalten-ID auflösen. */
async function requireBoardAdminByColumn(
  columnId: string,
): Promise<{ ok: true; userId: string } | { ok: false }> {
  const col = await prisma.boardColumn.findUnique({ where: { id: columnId }, select: { boardId: true } });
  return requireBoardAdmin(col?.boardId);
}

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

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

/** Benachrichtigt zugewiesene Personen (außer Auslöser). */
async function notifyAssignees(taskId: string, actorId: string, title: string, body: string) {
  const rows = await prisma.taskAssignee.findMany({
    where: { taskId, userId: { not: actorId } },
    select: { userId: true },
  });
  const ids = rows.map((r) => r.userId);
  if (ids.length > 0) {
    await createNotifications(ids, { type: "TASK", title, body, link: "/kanban" });
  }
}

// ── Aufgaben ────────────────────────────────────────────────────────────────
export type TaskInput = {
  columnId: string;
  title: string;
  description?: string;
  priority?: Priority;
  assigneeIds?: string[];
  deadline?: string | null;
  projectId?: string | null;
  labelIds?: string[];
  color?: string | null;
};
export type TaskResult = { task?: TaskDTO; error?: string };

export async function createTaskAction(input: TaskInput): Promise<TaskResult> {
  const auth = await requireManage();
  if (!auth.ok) return { error: "Keine Berechtigung." };

  const title = input.title.trim();
  if (!title) return { error: "Titel erforderlich." };

  const column = await prisma.boardColumn.findUnique({
    where: { id: input.columnId },
    select: { id: true, boardId: true },
  });
  if (!column) return { error: "Spalte nicht gefunden." };

  const priority = PRIORITIES.includes(input.priority as Priority)
    ? (input.priority as Priority)
    : "MEDIUM";
  const assigneeIds = await validUserIds(input.assigneeIds);
  const count = await prisma.task.count({ where: { columnId: column.id } });

  const task = await prisma.task.create({
    data: {
      boardId: column.boardId,
      columnId: column.id,
      title: title.slice(0, 200),
      description: input.description?.trim() || null,
      priority,
      deadline: parseDeadline(input.deadline),
      color: safeColor(input.color),
      projectId: input.projectId || null,
      creatorId: auth.userId,
      position: count,
      ...(assigneeIds.length ? { assignees: { create: assigneeIds.map((userId) => ({ userId })) } } : {}),
      ...(input.labelIds?.length
        ? { labels: { create: input.labelIds.map((labelId) => ({ labelId })) } }
        : {}),
    },
    select: { id: true },
  });

  await notifyAssignees(task.id, auth.userId, "Neue Aufgabe zugewiesen", title);

  const dto = await getTaskById(task.id);
  return dto ? { task: dto } : { error: "Fehler beim Laden." };
}

export async function updateTaskAction(
  taskId: string,
  patch: Partial<Omit<TaskInput, "columnId">>,
): Promise<TaskResult> {
  const auth = await requireManage();
  if (!auth.ok) return { error: "Keine Berechtigung." };

  const existing = await prisma.task.findUnique({
    where: { id: taskId },
    select: { id: true, title: true },
  });
  if (!existing) return { error: "Aufgabe nicht gefunden." };

  const data: Record<string, unknown> = {};
  if (patch.title !== undefined) {
    const t = patch.title.trim();
    if (!t) return { error: "Titel erforderlich." };
    data.title = t.slice(0, 200);
  }
  if (patch.description !== undefined) data.description = patch.description?.trim() || null;
  if (patch.priority !== undefined && PRIORITIES.includes(patch.priority)) data.priority = patch.priority;
  if (patch.deadline !== undefined) data.deadline = parseDeadline(patch.deadline);
  if (patch.color !== undefined) data.color = safeColor(patch.color);
  if (patch.projectId !== undefined) data.projectId = patch.projectId || null;

  const assigneeIds = patch.assigneeIds !== undefined ? await validUserIds(patch.assigneeIds) : null;

  await prisma.$transaction(async (tx) => {
    await tx.task.update({ where: { id: taskId }, data });
    if (patch.labelIds !== undefined) {
      await tx.taskLabel.deleteMany({ where: { taskId } });
      if (patch.labelIds.length) {
        await tx.taskLabel.createMany({ data: patch.labelIds.map((labelId) => ({ taskId, labelId })) });
      }
    }
    if (assigneeIds !== null) {
      await tx.taskAssignee.deleteMany({ where: { taskId } });
      if (assigneeIds.length) {
        await tx.taskAssignee.createMany({ data: assigneeIds.map((userId) => ({ taskId, userId })) });
      }
    }
  });

  await notifyAssignees(
    taskId,
    auth.userId,
    "Aufgabe aktualisiert",
    (data.title as string) ?? existing.title,
  );

  const dto = await getTaskById(taskId);
  return dto ? { task: dto } : { error: "Fehler beim Laden." };
}

export async function moveTaskAction(
  taskId: string,
  toColumnId: string,
  orderedIds: string[],
): Promise<{ error?: string }> {
  const auth = await requireManage();
  if (!auth.ok) return { error: "Keine Berechtigung." };

  const [task, column] = await Promise.all([
    prisma.task.findUnique({ where: { id: taskId }, select: { boardId: true, columnId: true, title: true } }),
    prisma.boardColumn.findUnique({ where: { id: toColumnId }, select: { boardId: true, name: true } }),
  ]);
  if (!task || !column || task.boardId !== column.boardId) return { error: "Ungültige Verschiebung." };

  const valid = await prisma.task.findMany({
    where: { id: { in: orderedIds }, boardId: column.boardId },
    select: { id: true },
  });
  const validIds = new Set(valid.map((v) => v.id));
  const ordered = orderedIds.filter((id) => validIds.has(id));

  await prisma.$transaction([
    prisma.task.update({ where: { id: taskId }, data: { columnId: toColumnId } }),
    ...ordered.map((id, index) => prisma.task.update({ where: { id }, data: { position: index } })),
  ]);

  if (task.columnId !== toColumnId) {
    await notifyAssignees(taskId, auth.userId, `Aufgabe nach „${column.name}“ verschoben`, task.title);
  }
  return {};
}

export async function deleteTaskAction(taskId: string): Promise<{ error?: string }> {
  const auth = await requireManage();
  if (!auth.ok) return { error: "Keine Berechtigung." };
  await prisma.task.deleteMany({ where: { id: taskId } });
  return {};
}

export async function addCommentAction(
  taskId: string,
  body: string,
): Promise<{ comment?: TaskCommentDTO; error?: string }> {
  const session = await requireSession();
  if (!session.user.permissions?.includes("kanban.comment")) return { error: "Keine Berechtigung." };
  const text = body.trim();
  if (!text) return { error: "Leerer Kommentar." };

  const task = await prisma.task.findUnique({ where: { id: taskId }, select: { id: true, title: true } });
  if (!task) return { error: "Aufgabe nicht gefunden." };

  const c = await prisma.taskComment.create({
    data: { taskId, authorId: session.user.id, body: text.slice(0, 2000) },
    select: {
      id: true,
      body: true,
      authorId: true,
      createdAt: true,
      author: { select: { name: true, displayName: true, avatarUrl: true } },
    },
  });

  await notifyAssignees(taskId, session.user.id, `Neuer Kommentar zu „${task.title}“`, text.slice(0, 120));

  return {
    comment: {
      id: c.id,
      body: c.body,
      authorId: c.authorId,
      authorName: c.author.displayName ?? c.author.name,
      authorAvatar: c.author.avatarUrl,
      createdAt: c.createdAt.toISOString(),
    },
  };
}

/** Kommentar löschen — durch den Autor selbst oder den Board-Admin. */
export async function deleteCommentAction(commentId: string): Promise<{ error?: string }> {
  const session = await requireSession();
  const comment = await prisma.taskComment.findUnique({
    where: { id: commentId },
    select: { authorId: true, task: { select: { boardId: true } } },
  });
  if (!comment) return { error: "Kommentar nicht gefunden." };

  if (comment.authorId !== session.user.id) {
    const admin = await requireBoardAdmin(comment.task.boardId);
    if (!admin.ok) return { error: "Keine Berechtigung." };
  }
  await prisma.taskComment.delete({ where: { id: commentId } });
  return {};
}

// ── Spalten ─────────────────────────────────────────────────────────────────
export async function createColumnAction(
  boardId: string,
  name: string,
): Promise<{ column?: ColumnDTO; error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Spalten verwalten." };
  const board = await prisma.board.findUnique({ where: { id: boardId }, select: { id: true } });
  if (!board) return { error: "Board nicht gefunden." };

  const position = await prisma.boardColumn.count({ where: { boardId } });
  if (position >= MAX_COLUMNS) return { error: `Maximal ${MAX_COLUMNS} Spalten pro Board.` };
  const col = await prisma.boardColumn.create({
    data: {
      boardId,
      key: `c-${Math.random().toString(36).slice(2, 9)}`,
      name: (name.trim() || "Neue Spalte").slice(0, 80),
      color: COLUMN_COLORS[position % COLUMN_COLORS.length],
      position,
    },
    select: { id: true, key: true, name: true, color: true },
  });
  return { column: { ...col, tasks: [] } };
}

export async function renameColumnAction(columnId: string, name: string): Promise<{ error?: string }> {
  const auth = await requireBoardAdminByColumn(columnId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Spalten verwalten." };
  const n = name.trim();
  if (!n) return { error: "Name erforderlich." };
  await prisma.boardColumn.update({ where: { id: columnId }, data: { name: n.slice(0, 80) } });
  return {};
}

export async function deleteColumnAction(columnId: string): Promise<{ error?: string }> {
  const auth = await requireBoardAdminByColumn(columnId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Spalten verwalten." };
  await prisma.boardColumn.deleteMany({ where: { id: columnId } });
  return {};
}

export async function setColumnColorAction(columnId: string, color: string): Promise<{ error?: string }> {
  const auth = await requireBoardAdminByColumn(columnId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Spalten verwalten." };
  await prisma.boardColumn.update({
    where: { id: columnId },
    data: { color: safeColor(color) ?? "#9ed7ff" },
  });
  return {};
}

// ── Labels ────────────────────────────────────────────────────────────────
export type LabelDTO = { id: string; name: string; color: string };

export async function createLabelAction(
  name: string,
  color: string,
  boardId?: string,
): Promise<{ label?: LabelDTO; error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Labels verwalten." };
  const n = name.trim();
  if (!n) return { error: "Name erforderlich." };
  const existing = await prisma.label.findUnique({ where: { name: n.slice(0, 60) }, select: { id: true } });
  if (existing) return { error: "Label existiert bereits." };
  const label = await prisma.label.create({
    data: { name: n.slice(0, 60), color: safeColor(color) ?? "#9ed7ff" },
    select: { id: true, name: true, color: true },
  });
  return { label };
}

export async function deleteLabelAction(labelId: string, boardId?: string): Promise<{ error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann Labels verwalten." };
  await prisma.label.deleteMany({ where: { id: labelId } });
  return {};
}

// ── Boards ──────────────────────────────────────────────────────────────────
export async function createBoardAction(
  name: string,
  icon?: string | null,
  color?: string | null,
): Promise<{ id?: string; error?: string }> {
  const session = await requireSession();
  if (!session.user.permissions?.includes("kanban.create_board")) {
    return { error: "Keine Berechtigung zum Erstellen von Boards." };
  }
  const auth = { ok: true as const, userId: session.user.id };
  // Neue Boards starten schlank (max. 5 Spalten); der Ersteller wird Board-Admin.
  const board = await prisma.board.create({
    data: {
      name: (name.trim() || "Neues Board").slice(0, 120),
      icon: icon?.slice(0, 40) || null,
      color: safeColor(color),
      adminId: auth.userId,
      columns: {
        create: DEFAULT_COLUMNS.slice(0, MAX_COLUMNS).map((c, i) => ({
          key: c.key,
          name: c.name,
          color: c.color,
          position: i,
        })),
      },
    },
    select: { id: true },
  });
  return { id: board.id };
}

export async function updateBoardAction(
  boardId: string,
  patch: { name?: string; icon?: string | null; color?: string | null },
): Promise<{ error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann das Board anpassen." };
  const data: Record<string, unknown> = {};
  if (patch.name !== undefined) {
    const n = patch.name.trim();
    if (!n) return { error: "Name erforderlich." };
    data.name = n.slice(0, 120);
  }
  if (patch.icon !== undefined) data.icon = patch.icon?.slice(0, 40) || null;
  if (patch.color !== undefined) data.color = safeColor(patch.color);
  await prisma.board.update({ where: { id: boardId }, data });
  return {};
}

export async function deleteBoardAction(boardId: string): Promise<{ error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Nur der Board-Admin kann das Board löschen." };
  const board = await prisma.board.findUnique({ where: { id: boardId }, select: { isDefault: true } });
  if (!board) return { error: "Board nicht gefunden." };
  if (board.isDefault) return { error: "Standard-Board kann nicht gelöscht werden." };
  await prisma.board.delete({ where: { id: boardId } });
  return {};
}

/** Board-Admin festlegen/entfernen (nur aktueller Admin oder Super-Admin). */
export async function setBoardAdminAction(
  boardId: string,
  adminUserId: string | null,
): Promise<{ error?: string }> {
  const auth = await requireBoardAdmin(boardId);
  if (!auth.ok) return { error: "Keine Berechtigung." };
  let adminId: string | null = null;
  if (adminUserId) {
    const u = await prisma.user.findUnique({ where: { id: adminUserId }, select: { id: true } });
    adminId = u?.id ?? null;
  }
  await prisma.board.update({ where: { id: boardId }, data: { adminId } });
  return {};
}
