import "server-only";
import { prisma } from "@/lib/prisma";
import type { TimeEntrySource, TimeEntryStatus } from "@prisma/client";

export type TimeEntryDTO = {
  id: string;
  projectId: string | null;
  projectName: string | null;
  startedAt: string;
  endedAt: string | null;
  durationSeconds: number;
  note: string | null;
  source: TimeEntrySource;
  status: TimeEntryStatus;
  running: boolean;
};

function mapEntry(e: {
  id: string;
  projectId: string | null;
  project: { name: string } | null;
  startedAt: Date;
  endedAt: Date | null;
  durationSeconds: number;
  note: string | null;
  source: TimeEntrySource;
  status: TimeEntryStatus;
}): TimeEntryDTO {
  return {
    id: e.id,
    projectId: e.projectId,
    projectName: e.project?.name ?? null,
    startedAt: e.startedAt.toISOString(),
    endedAt: e.endedAt ? e.endedAt.toISOString() : null,
    durationSeconds: e.durationSeconds,
    note: e.note,
    source: e.source,
    status: e.status,
    running: e.status === "RUNNING",
  };
}

const entrySelect = {
  id: true,
  projectId: true,
  project: { select: { name: true } },
  startedAt: true,
  endedAt: true,
  durationSeconds: true,
  note: true,
  source: true,
  status: true,
} as const;

/** Zeiteinträge eines Nutzers (neueste zuerst). */
export async function getMyTimeEntries(userId: string, take = 100): Promise<TimeEntryDTO[]> {
  const rows = await prisma.timeEntry.findMany({
    where: { userId },
    orderBy: { startedAt: "desc" },
    take,
    select: entrySelect,
  });
  return rows.map(mapEntry);
}

/** Laufender Timer des Nutzers (falls vorhanden). */
export async function getRunningEntry(userId: string): Promise<TimeEntryDTO | null> {
  const e = await prisma.timeEntry.findFirst({
    where: { userId, status: "RUNNING" },
    select: entrySelect,
  });
  return e ? mapEntry(e) : null;
}

export async function getTimeProjects(): Promise<{ id: string; name: string }[]> {
  return prisma.project.findMany({
    where: { status: { in: ["ACTIVE", "PLANNING", "ON_HOLD"] } },
    orderBy: { name: "asc" },
    select: { id: true, name: true },
  });
}

// Als "gebuchte Zeit" zählen laufende Timer-Ergebnisse (OPEN) + freigegebene (APPROVED).
const COUNTED = ["OPEN", "APPROVED"] as const;

/** Eigene noch offene (eingereichte) Einträge — warten auf Freigabe. */
export async function getMySubmittedEntries(userId: string): Promise<TimeEntryDTO[]> {
  const rows = await prisma.timeEntry.findMany({
    where: { userId, status: "SUBMITTED" },
    orderBy: { startedAt: "desc" },
    select: entrySelect,
  });
  return rows.map(mapEntry);
}

/** Offene Freigaben (für Vorgesetzte / Personalverwaltung). */
export type PendingTimeDTO = TimeEntryDTO & { userName: string; userId: string };
export async function getPendingTimeEntries(supervisorId?: string): Promise<PendingTimeDTO[]> {
  const rows = await prisma.timeEntry.findMany({
    where: { status: "SUBMITTED", ...(supervisorId ? { user: { supervisorId } } : {}) },
    orderBy: { startedAt: "desc" },
    select: { ...entrySelect, userId: true, user: { select: { name: true, displayName: true } } },
  });
  return rows.map((r) => ({ ...mapEntry(r), userId: r.userId, userName: r.user.displayName ?? r.user.name }));
}

/** Summe der erfassten Sekunden dieser Woche (gezählte Einträge). */
export async function getWeekSeconds(userId: string): Promise<number> {
  const now = new Date();
  const day = (now.getDay() + 6) % 7;
  const monday = new Date(now);
  monday.setHours(0, 0, 0, 0);
  monday.setDate(now.getDate() - day);
  const rows = await prisma.timeEntry.findMany({
    where: { userId, startedAt: { gte: monday }, status: { in: [...COUNTED] } },
    select: { durationSeconds: true },
  });
  return rows.reduce((s, r) => s + r.durationSeconds, 0);
}

export type DayInfo = { timer: number; manual: number; absence: string | null };
export type YearStats = {
  year: number;
  month: number;
  week: number;
  perDay: Record<string, DayInfo>;
};

function dayKeyOf(d: Date): string {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}

/** Jahres-Heatmap: pro Tag Stempel-/Manuell-Zeit + genehmigte Abwesenheit. */
export async function getYearStats(userId: string, year: number): Promise<YearStats> {
  const from = new Date(year, 0, 1);
  const to = new Date(year + 1, 0, 1);

  const [entries, absences] = await Promise.all([
    prisma.timeEntry.findMany({
      where: { userId, startedAt: { gte: from, lt: to }, status: { in: [...COUNTED] } },
      select: { startedAt: true, durationSeconds: true, source: true },
    }),
    prisma.absenceRequest.findMany({
      where: { userId, status: "APPROVED", startDate: { lt: to }, endDate: { gte: from } },
      select: { startDate: true, endDate: true, type: true },
    }),
  ]);

  const perDay: Record<string, DayInfo> = {};
  const get = (k: string) => (perDay[k] ??= { timer: 0, manual: 0, absence: null });

  for (const r of entries) {
    const info = get(dayKeyOf(r.startedAt));
    if (r.source === "MANUAL") info.manual += r.durationSeconds;
    else info.timer += r.durationSeconds;
  }
  for (const a of absences) {
    const d = new Date(Math.max(a.startDate.getTime(), from.getTime()));
    const end = new Date(Math.min(a.endDate.getTime(), to.getTime() - 1));
    d.setHours(12, 0, 0, 0);
    while (d <= end) {
      get(dayKeyOf(d)).absence = a.type;
      d.setDate(d.getDate() + 1);
    }
  }

  const now = new Date();
  const curMonth = now.getFullYear() === year ? now.getMonth() : 11;
  const dow = (now.getDay() + 6) % 7;
  const monday = new Date(now);
  monday.setHours(0, 0, 0, 0);
  monday.setDate(now.getDate() - dow);

  let total = 0;
  let month = 0;
  let week = 0;
  for (const [k, v] of Object.entries(perDay)) {
    const secs = v.timer + v.manual;
    total += secs;
    if (Number(k.slice(5, 7)) - 1 === curMonth) month += secs;
    if (new Date(`${k}T12:00`) >= monday) week += secs;
  }

  return { year: total, month, week, perDay };
}
