import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
import {
  requireAuth,
  requirePermission,
  requireAnyPermission,
} from "../../middleware/auth";
import { success, error, parseId } from "../../utils/response";
import { parseBody } from "../../schemas/common";
import { AuthData } from "../../types";
import { logAudit } from "../../services/audit";
import {
  CreatePlanEntrySchema,
  UpdatePlanEntrySchema,
  CreatePlanOverrideSchema,
  UpdatePlanOverrideSchema,
  PlanRangeQuerySchema,
  PlanGridQuerySchema,
  BulkPlanEntrySchema,
} from "../../schemas/plan.schema";
import {
  resolveGrid,
  listPlanUsers,
  createEntry,
  updateEntry,
  deleteEntry,
  createOverride,
  updateOverride,
  deleteOverride,
  listEntries,
  listOverrides,
  bulkCreateEntries,
} from "../../services/plan.service";
import {
  CreatePlanCategorySchema,
  UpdatePlanCategorySchema,
} from "../../schemas/planCategory.schema";
import {
  listPlanCategories,
  createPlanCategory,
  updatePlanCategory,
  deletePlanCategory,
} from "../../services/planCategory.service";

const MAX_RANGE_DAYS = 92;

// NOTE: AuthData carries `roleName` (not `role` — that lives on JwtPayload).
// The previous `authData.role` read was always undefined, so admins were
// wrongly scoped to their own plan rows on the /entries and /overrides
// endpoints. Typed (not `any`) so this can't silently regress.
function isAdminLike(authData: AuthData | undefined): boolean {
  return authData?.roleName === "admin";
}

function forceFromQuery(query: unknown): boolean {
  if (!query || typeof query !== "object") return false;
  return (query as { force?: unknown }).force === "1";
}

function daysBetween(from: string, to: string): number {
  const a = new Date(from + "T00:00:00.000Z").getTime();
  const b = new Date(to + "T00:00:00.000Z").getTime();
  return Math.round((b - a) / (1000 * 60 * 60 * 24));
}

export default async function planRoutes(app: FastifyInstance) {
  app.addHook("preHandler", requireAuth);

  // --- GET /plan/users ---
  app.get(
    "/users",
    {
      preHandler: requireAnyPermission(
        "attendance.manage",
        "attendance.record",
      ),
    },
    async (_request: FastifyRequest, reply: FastifyReply) => {
      const users = await listPlanUsers();
      return success(reply, users);
    },
  );

  // --- GET /plan/grid ---
  app.get(
    "/grid",
    {
      preHandler: requireAnyPermission(
        "attendance.manage",
        "attendance.record",
      ),
    },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const parsed = PlanGridQuerySchema.safeParse(request.query);
      if (!parsed.success) {
        return error(reply, parsed.error.issues[0].message, 400);
      }
      const { date_from, date_to, view } = parsed.data;
      if (daysBetween(date_from, date_to) > MAX_RANGE_DAYS) {
        return error(reply, "Maximální rozsah je 92 dní", 400);
      }
      const users = await listPlanUsers();
      const cells = await resolveGrid(
        users.map((u) => u.id),
        date_from,
        date_to,
      );
      return success(reply, { view, date_from, date_to, users, cells });
    },
  );

  // --- GET /plan/entries ---
  app.get(
    "/entries",
    {
      preHandler: requireAnyPermission(
        "attendance.manage",
        "attendance.record",
      ),
    },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const parsed = PlanRangeQuerySchema.safeParse(request.query);
      if (!parsed.success)
        return error(reply, parsed.error.issues[0].message, 400);
      const rows = await listEntries(
        parsed.data,
        request.authData!.userId,
        isAdminLike(request.authData),
      );
      return success(reply, rows);
    },
  );

  // --- GET /plan/overrides ---
  app.get(
    "/overrides",
    {
      preHandler: requireAnyPermission(
        "attendance.manage",
        "attendance.record",
      ),
    },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const parsed = PlanRangeQuerySchema.safeParse(request.query);
      if (!parsed.success)
        return error(reply, parsed.error.issues[0].message, 400);
      const rows = await listOverrides(
        parsed.data,
        request.authData!.userId,
        isAdminLike(request.authData),
      );
      return success(reply, rows);
    },
  );

  // --- GET /plan/categories (any planner can read; needed for display) ---
  app.get(
    "/categories",
    {
      preHandler: requireAnyPermission(
        "attendance.manage",
        "attendance.record",
      ),
    },
    async (_request: FastifyRequest, reply: FastifyReply) => {
      const cats = await listPlanCategories(true);
      return success(reply, cats);
    },
  );

  // --- POST /plan/categories ---
  app.post(
    "/categories",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const body = parseBody(CreatePlanCategorySchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const result = await createPlanCategory(body.data!);
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "plan_category",
        entityId: result.data.id,
        newValues: result.data,
        description: `Kategorie: ${result.data.label}`,
      });
      return success(reply, result.data, 201, "Kategorie vytvořena");
    },
  );

  // --- PATCH /plan/categories/:id ---
  app.patch(
    "/categories/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const body = parseBody(UpdatePlanCategorySchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const result = await updatePlanCategory(id, body.data!);
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "plan_category",
        entityId: id,
        newValues: result.data,
        description: `Kategorie: ${result.data.label}`,
      });
      return success(reply, result.data, 200, "Kategorie aktualizována");
    },
  );

  // --- DELETE /plan/categories/:id (hard delete; blocked if in use) ---
  app.delete(
    "/categories/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const result = await deletePlanCategory(id);
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "plan_category",
        entityId: id,
        description: "Kategorie smazána",
      });
      return success(reply, { ok: true }, 200, "Kategorie smazána");
    },
  );

  // --- POST /plan/entries ---
  app.post(
    "/entries",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const body = parseBody(CreatePlanEntrySchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const force = forceFromQuery(request.query);
      const result = await createEntry(
        body.data!,
        request.authData!.userId,
        force,
      );
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "work_plan_entry",
        entityId: result.data.id,
        newValues: result.data,
        description: result.description,
      });
      return success(reply, result.data, 201, "Plán vytvořen");
    },
  );

  // --- POST /plan/entries/bulk ---
  app.post(
    "/entries/bulk",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const body = parseBody(BulkPlanEntrySchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const result = await bulkCreateEntries(
        body.data!,
        request.authData!.userId,
      );
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "work_plan_entry",
        description: `Hromadné přiřazení: ${body.data!.category} — ${result.data.users} zaměstnanců, ${body.data!.date_from}–${body.data!.date_to} (${result.data.created_days} dní)`,
      });
      return success(reply, result.data, 201, "Hromadné přiřazení dokončeno");
    },
  );

  // --- PATCH /plan/entries/:id ---
  app.patch(
    "/entries/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const body = parseBody(UpdatePlanEntrySchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const force = forceFromQuery(request.query);
      const result = await updateEntry(
        id,
        body.data!,
        request.authData!.userId,
        force,
      );
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "work_plan_entry",
        entityId: id,
        oldValues:
          (result.oldData as Record<string, unknown> | null) ?? undefined,
        newValues: result.data as Record<string, unknown>,
        description: result.description,
      });
      return success(reply, result.data, 200, "Plán aktualizován");
    },
  );

  // --- DELETE /plan/entries/:id ---
  app.delete(
    "/entries/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const force = forceFromQuery(request.query);
      const result = await deleteEntry(id, request.authData!.userId, force);
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "work_plan_entry",
        entityId: id,
        oldValues:
          (result.oldData as Record<string, unknown> | null) ?? undefined,
        description: result.description,
      });
      return success(reply, { ok: true }, 200, "Plán smazán");
    },
  );

  // --- POST /plan/overrides ---
  app.post(
    "/overrides",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const body = parseBody(CreatePlanOverrideSchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const force = forceFromQuery(request.query);
      const result = await createOverride(
        body.data!,
        request.authData!.userId,
        force,
      );
      if ("error" in result) return error(reply, result.error, result.status);

      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "work_plan_override",
        entityId: result.data.id,
        newValues: result.data,
        description: result.description,
      });
      return success(reply, result.data, 201, "Přepsání dne vytvořeno");
    },
  );

  // --- PATCH /plan/overrides/:id ---
  app.patch(
    "/overrides/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const body = parseBody(UpdatePlanOverrideSchema, request.body);
      if ("error" in body) return error(reply, body.error, 400);
      const force = forceFromQuery(request.query);
      const result = await updateOverride(
        id,
        body.data!,
        request.authData!.userId,
        force,
      );
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "work_plan_override",
        entityId: id,
        oldValues:
          (result.oldData as Record<string, unknown> | null) ?? undefined,
        newValues: result.data,
        description: result.description,
      });
      return success(reply, result.data, 200, "Přepsání aktualizováno");
    },
  );

  // --- DELETE /plan/overrides/:id ---
  app.delete(
    "/overrides/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request: FastifyRequest, reply: FastifyReply) => {
      const id = parseId((request.params as any).id, reply);
      if (id === null) return;
      const force = forceFromQuery(request.query);
      const result = await deleteOverride(id, request.authData!.userId, force);
      if ("error" in result) return error(reply, result.error, result.status);
      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "work_plan_override",
        entityId: id,
        oldValues:
          (result.oldData as Record<string, unknown> | null) ?? undefined,
        description: result.description,
      });
      return success(reply, { ok: true }, 200, "Přepsání smazáno");
    },
  );
}
