import { Router, type Request } from "express";
import { z } from "zod";
import { requireAdmin, requireAuth } from "../auth/middleware";
import {
  addEmployee,
  deactivateEmployee,
  findEmployeeById,
  listEmployees,
} from "../services/employees";
import { eventsInRange, summarize } from "../services/attendance";
import { parseRange } from "../util/dateRange";
import { payrollToCsv, sessionsToCsv } from "../util/csv";
import {
  assignEmployeeToSlot,
  createSlot,
  listAdminSlotsForPeriod,
  removeEmployeeFromSlot,
  updateSlot,
} from "../services/shiftPlanning";
import { getCurrentRate, setRate } from "../services/payRates";
import {
  getPeriodOverview,
  lockAndFinalizePayroll,
  markPaid,
  reopenPayroll,
  saveDraftAdjustments,
} from "../services/payroll";

export const adminRouter = Router();
adminRouter.use(requireAuth, requireAdmin);

adminRouter.get("/employees", (_req, res) => {
  const employees = listEmployees().map((employee) => ({
    ...employee,
    hourly_rate: getCurrentRate(employee.id),
  }));
  res.json({ employees });
});

const rateSchema = z.object({
  hourly_rate: z.number().nonnegative(),
  valid_from: z.string().optional(),
});

adminRouter.post("/employees/:id/rate", (req, res) => {
  const id = Number(req.params.id);
  const parsed = rateSchema.safeParse(req.body);
  if (!Number.isInteger(id) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data." });
    return;
  }
  try {
    setRate(id, parsed.data.hourly_rate, parsed.data.valid_from);
    res.status(201).json({ hourly_rate: getCurrentRate(id) });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Uložení se nezdařilo" });
  }
});

const addEmployeeSchema = z.object({
  email: z.string().email(),
  name: z.string().trim().min(1).optional(),
});

adminRouter.post("/employees", (req, res) => {
  const parsed = addEmployeeSchema.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: "Valid email is required" });
    return;
  }
  const employee = addEmployee(parsed.data.email, parsed.data.name ?? null);
  res.status(201).json({ employee });
});

adminRouter.delete("/employees/:id", (req, res) => {
  const id = Number(req.params.id);
  if (!Number.isInteger(id)) {
    res.status(400).json({ error: "Invalid employee id" });
    return;
  }
  const removed = deactivateEmployee(id);
  if (!removed) {
    res.status(404).json({ error: "Employee not found" });
    return;
  }
  res.status(204).end();
});

adminRouter.get("/stats", (req, res) => {
  const { fromIso, toIso } = parseRange(req.query as Record<string, unknown>);
  const now = new Date();
  const employees = listEmployees();

  const perEmployee = employees.map((employee) => {
    const events = eventsInRange(employee.id, fromIso, toIso);
    const stats = summarize(events, now);
    return {
      employee,
      totalWorkedMs: stats.totalWorkedMs,
      totalBreakMs: stats.totalBreakMs,
      shiftCount: stats.shiftCount,
    };
  });

  const totals = perEmployee.reduce(
    (acc, e) => ({
      totalWorkedMs: acc.totalWorkedMs + e.totalWorkedMs,
      totalBreakMs: acc.totalBreakMs + e.totalBreakMs,
      shiftCount: acc.shiftCount + e.shiftCount,
    }),
    { totalWorkedMs: 0, totalBreakMs: 0, shiftCount: 0 }
  );

  res.json({ range: { from: fromIso, to: toIso }, totals, employees: perEmployee });
});

adminRouter.get("/stats/:id", (req, res) => {
  const id = Number(req.params.id);
  const employee = Number.isInteger(id) ? findEmployeeById(id) : undefined;
  if (!employee) {
    res.status(404).json({ error: "Employee not found" });
    return;
  }
  const { fromIso, toIso } = parseRange(req.query as Record<string, unknown>);
  const events = eventsInRange(employee.id, fromIso, toIso);
  const stats = summarize(events, new Date());
  res.json({ employee, range: { from: fromIso, to: toIso }, ...stats });
});

adminRouter.get("/stats/:id/export", (req, res) => {
  const id = Number(req.params.id);
  const employee = Number.isInteger(id) ? findEmployeeById(id) : undefined;
  if (!employee) {
    res.status(404).json({ error: "Employee not found" });
    return;
  }
  const { fromIso, toIso } = parseRange(req.query as Record<string, unknown>);
  const events = eventsInRange(employee.id, fromIso, toIso);
  const { sessions } = summarize(events, new Date());
  const csv = sessionsToCsv(sessions);

  const period = fromIso.slice(0, 7);
  const safeName = employee.email.replace(/[^a-z0-9.@-]/gi, "_");

  res.setHeader("Content-Type", "text/csv; charset=utf-8");
  res.setHeader("Content-Disposition", `attachment; filename="${safeName}_${period}.csv"`);
  res.send("﻿" + csv); // BOM so Excel opens the Czech diacritics as UTF-8
});

/* ---------- shift planning ---------- */

function periodParam(req: Request): string {
  const period = req.query.period;
  if (typeof period === "string" && /^\d{4}-\d{2}$/.test(period)) return period;
  const now = new Date();
  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
}

adminRouter.get("/shifts", (req, res) => {
  res.json({ slots: listAdminSlotsForPeriod(periodParam(req)) });
});

const slotSchema = z.object({
  date: z.string(),
  start_time: z.string(),
  end_time: z.string(),
  capacity: z.number(),
  note: z.string().optional(),
});

adminRouter.post("/shifts", (req, res) => {
  const parsed = slotSchema.safeParse(req.body);
  if (!parsed.success) {
    res.status(400).json({ error: "Neplatná data směny." });
    return;
  }
  try {
    const id = createSlot(parsed.data);
    res.status(201).json({ id });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Vytvoření se nezdařilo" });
  }
});

adminRouter.patch("/shifts/:slotId", (req, res) => {
  const slotId = Number(req.params.slotId);
  const parsed = slotSchema.safeParse(req.body);
  if (!Number.isInteger(slotId) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data směny." });
    return;
  }
  try {
    updateSlot(slotId, parsed.data);
    res.json({ ok: true });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Úprava se nezdařila" });
  }
});

adminRouter.post("/shifts/:slotId/assign", (req, res) => {
  const slotId = Number(req.params.slotId);
  const employeeId = Number(req.body?.employeeId);
  if (!Number.isInteger(slotId) || !Number.isInteger(employeeId)) {
    res.status(400).json({ error: "Neplatný požadavek." });
    return;
  }
  try {
    assignEmployeeToSlot(slotId, employeeId);
    res.status(201).json({ ok: true });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Přiřazení se nezdařilo" });
  }
});

adminRouter.delete("/shifts/:slotId/assign/:employeeId", (req, res) => {
  const slotId = Number(req.params.slotId);
  const employeeId = Number(req.params.employeeId);
  if (!Number.isInteger(slotId) || !Number.isInteger(employeeId)) {
    res.status(400).json({ error: "Neplatný požadavek." });
    return;
  }
  try {
    removeEmployeeFromSlot(slotId, employeeId, req.user!.email);
    res.status(204).end();
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Odebrání se nezdařilo" });
  }
});

/* ---------- payroll ---------- */

adminRouter.get("/payroll", (req, res) => {
  res.json({ rows: getPeriodOverview(periodParam(req)) });
});

const adjustmentsSchema = z.object({
  period: z.string().regex(/^\d{4}-\d{2}$/),
  tips_amount: z.number(),
  bonus_amount: z.number(),
  other_amount: z.number(),
});

adminRouter.post("/payroll/:employeeId/adjustments", (req, res) => {
  const employeeId = Number(req.params.employeeId);
  const parsed = adjustmentsSchema.safeParse(req.body);
  if (!Number.isInteger(employeeId) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data." });
    return;
  }
  try {
    const payroll = saveDraftAdjustments(employeeId, parsed.data.period, {
      tips_amount: parsed.data.tips_amount,
      bonus_amount: parsed.data.bonus_amount,
      other_amount: parsed.data.other_amount,
    });
    res.json({ payroll });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Uložení se nezdařilo" });
  }
});

const periodBodySchema = z.object({ period: z.string().regex(/^\d{4}-\d{2}$/) });

adminRouter.post("/payroll/:employeeId/lock", (req, res) => {
  const employeeId = Number(req.params.employeeId);
  const parsed = periodBodySchema.safeParse(req.body);
  if (!Number.isInteger(employeeId) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data." });
    return;
  }
  try {
    const payroll = lockAndFinalizePayroll(employeeId, parsed.data.period);
    res.json({ payroll });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Uzamčení se nezdařilo" });
  }
});

adminRouter.post("/payroll/:employeeId/reopen", (req, res) => {
  const employeeId = Number(req.params.employeeId);
  const parsed = periodBodySchema.safeParse(req.body);
  if (!Number.isInteger(employeeId) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data." });
    return;
  }
  try {
    const payroll = reopenPayroll(employeeId, parsed.data.period);
    res.json({ payroll });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Odemknutí se nezdařilo" });
  }
});

adminRouter.post("/payroll/:employeeId/paid", (req, res) => {
  const employeeId = Number(req.params.employeeId);
  const parsed = periodBodySchema.safeParse(req.body);
  if (!Number.isInteger(employeeId) || !parsed.success) {
    res.status(400).json({ error: "Neplatná data." });
    return;
  }
  try {
    const payroll = markPaid(employeeId, parsed.data.period);
    res.json({ payroll });
  } catch (err) {
    res.status(400).json({ error: err instanceof Error ? err.message : "Označení se nezdařilo" });
  }
});

adminRouter.get("/payroll/export", (req, res) => {
  const period = periodParam(req);
  const csv = payrollToCsv(getPeriodOverview(period));

  res.setHeader("Content-Type", "text/csv; charset=utf-8");
  res.setHeader("Content-Disposition", `attachment; filename="mzdy_${period}.csv"`);
  res.send("﻿" + csv);
});
