import { FastifyInstance } from "fastify";
import prisma from "../../config/database";
import { requireAuth, requirePermission } from "../../middleware/auth";
import { logAudit } from "../../services/audit";
import { success, error, parseId, paginated } from "../../utils/response";
import { parsePagination, buildPaginationMeta } from "../../utils/pagination";
import { parseBody } from "../../schemas/common";
import {
  AttendanceNotesSchema,
  AttendanceUpdateAddressSchema,
  AttendanceSwitchProjectSchema,
  AttendanceBalancesSchema,
  AttendanceBulkSchema,
  AttendanceLeaveSchema,
  AttendancePunchSchema,
  CreateAttendanceSchema,
  UpdateAttendanceSchema,
} from "../../schemas/attendance.schema";
import * as attendanceService from "../../services/attendance.service";
import { localMonthStr } from "../../utils/date";

export default async function attendanceRoutes(
  fastify: FastifyInstance,
): Promise<void> {
  // GET /api/admin/attendance/status — clock-in/out page data
  fastify.get(
    "/status",
    { preHandler: requireAuth },
    async (request, reply) => {
      const authData = request.authData!;
      const data = await attendanceService.getStatus(authData.userId);
      return success(reply, data);
    },
  );

  // POST /api/admin/attendance/notes — save shift notes
  fastify.post(
    "/notes",
    { preHandler: requireAuth },
    async (request, reply) => {
      const authData = request.authData!;
      const parsed = parseBody(AttendanceNotesSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const result = await attendanceService.saveNotes(
        authData.userId,
        body.notes ? String(body.notes) : null,
      );
      if ("error" in result) return error(reply, result.error!, 400);
      return success(reply, null, 200, "Poznámka uložena");
    },
  );

  // POST /api/admin/attendance/update-address — update GPS address after punch
  fastify.post(
    "/update-address",
    { preHandler: requireAuth },
    async (request, reply) => {
      const authData = request.authData!;
      const parsed = parseBody(AttendanceUpdateAddressSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const result = await attendanceService.updateAddress(
        authData.userId,
        body.address ?? null,
        body.punch_action,
      );
      if ("error" in result) return error(reply, result.error!, 404);
      return success(reply, null, 200, "Adresa aktualizována");
    },
  );

  // POST /api/admin/attendance/switch-project — switch active project on current shift
  fastify.post(
    "/switch-project",
    { preHandler: requireAuth },
    async (request, reply) => {
      const authData = request.authData!;
      const parsed = parseBody(AttendanceSwitchProjectSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const newProjectId = body.project_id ? Number(body.project_id) : null;
      const result = await attendanceService.switchProject(
        authData.userId,
        newProjectId,
      );
      if ("error" in result) return error(reply, result.error!, 400);
      return success(reply, null, 200, "Projekt přepnut");
    },
  );

  // GET /api/admin/attendance
  fastify.get("/", { preHandler: requireAuth }, async (request, reply) => {
    const query = request.query as Record<string, unknown>;
    const authData = request.authData!;
    const action = query.action ? String(query.action) : null;

    // --- action=balances: leave balance overview for all users ---
    if (action === "balances") {
      if (!authData.permissions.includes("attendance.balances")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const yr = Number(query.year) || new Date().getFullYear();
      const data = await attendanceService.getBalances(yr);
      return success(reply, data);
    }

    // --- action=workfund: monthly work fund overview ---
    if (action === "workfund") {
      if (!authData.permissions.includes("attendance.manage")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const yr = Number(query.year) || new Date().getFullYear();
      const data = await attendanceService.getWorkfund(yr);
      return success(reply, data);
    }

    // --- action=project_report: monthly project hours ---
    if (action === "project_report") {
      if (!authData.permissions.includes("attendance.manage")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const yr = Number(query.year) || new Date().getFullYear();
      const data = await attendanceService.getProjectReport(yr);
      return success(reply, data);
    }

    // --- action=print: attendance print data for admin ---
    if (action === "print") {
      if (!authData.permissions.includes("attendance.manage")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }

      const monthStr = query.month
        ? String(query.month)
        : localMonthStr(new Date());
      const parsedUserId = query.user_id ? Number(query.user_id) : null;
      const filterUserId =
        parsedUserId !== null && Number.isFinite(parsedUserId)
          ? parsedUserId
          : null;
      const data = await attendanceService.getPrintData(monthStr, filterUserId);
      return success(reply, data);
    }

    // --- action=attendance_users: users with attendance.record permission ---
    if (action === "attendance_users") {
      if (
        !authData.permissions.includes("attendance.manage") &&
        !authData.permissions.includes("attendance.record")
      ) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const users = await prisma.users.findMany({
        where: {
          is_active: true,
          roles: {
            role_permissions: {
              some: { permissions: { name: "attendance.record" } },
            },
          },
        },
        select: { id: true, first_name: true, last_name: true, username: true },
        orderBy: { last_name: "asc" },
      });
      return success(
        reply,
        users.map((u) => ({
          id: u.id,
          first_name: u.first_name,
          last_name: u.last_name,
          username: u.username,
        })),
      );
    }

    // --- action=projects: active projects for attendance project switching ---
    if (action === "projects") {
      const data = await attendanceService.getActiveProjects();
      return success(reply, data);
    }

    // --- action=project_logs: get project logs for a specific attendance record ---
    if (action === "project_logs") {
      if (!authData.permissions.includes("attendance.record")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const attendanceId = Number(query.attendance_id);
      if (!attendanceId || !Number.isFinite(attendanceId))
        return error(reply, "Chybí attendance_id", 400);
      // Ownership check: a non-admin may only read logs for their own
      // attendance record (mirrors the action=location path below).
      const parentRecord =
        await attendanceService.getLocationRecord(attendanceId);
      if (!parentRecord) return error(reply, "Záznam nenalezen", 404);
      const isAdmin = authData.permissions.includes("attendance.manage");
      if (parentRecord.user_id !== authData.userId && !isAdmin) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      const data = await attendanceService.getProjectLogs(attendanceId);
      return success(reply, data);
    }

    // --- action=location: single record with GPS data ---
    if (action === "location") {
      const id = Number(query.id);
      if (!id || !Number.isFinite(id))
        return error(reply, "Chybí id záznamu", 400);
      const record = await attendanceService.getLocationRecord(id);
      if (!record) return error(reply, "Záznam nenalezen", 404);
      const isAdmin = authData.permissions.includes("attendance.manage");
      if (record.user_id !== authData.userId && !isAdmin) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }
      return success(reply, record);
    }

    // --- Default: paginated records list ---
    // maxLimit must allow a COMPLETE month: the admin month view fetches all
    // records at once and computes the KPI cards / summary totals client-side
    // (useAttendanceAdmin). With the default 100-row cap, months with more
    // than 100 records were silently truncated (newest-first), so the screen
    // totals dropped the earliest days while the print stayed complete.
    const { page, limit, skip, order } = parsePagination(query, {
      maxLimit: 2000,
    });
    const isAdmin = authData.permissions.includes("attendance.manage");
    const parsedUserId = query.user_id ? Number(query.user_id) : undefined;
    const userId =
      parsedUserId !== undefined && Number.isFinite(parsedUserId)
        ? parsedUserId
        : undefined;

    const result = await attendanceService.listAttendance({
      page,
      limit,
      skip,
      order,
      userId,
      isAdmin,
      authUserId: authData.userId,
      month: query.month
        ? String(query.month).includes("-")
          ? Number(String(query.month).split("-")[1])
          : Number(query.month)
        : undefined,
      year: query.month
        ? String(query.month).includes("-")
          ? Number(String(query.month).split("-")[0])
          : query.year
            ? Number(query.year)
            : undefined
        : query.year
          ? Number(query.year)
          : undefined,
    });

    return paginated(
      reply,
      result.records,
      buildPaginationMeta(result.total, result.page, result.limit),
    );
  });

  // POST /api/admin/attendance
  fastify.post("/", { preHandler: requireAuth }, async (request, reply) => {
    const rawBody = request.body as Record<string, unknown>;
    const authData = request.authData!;
    const postQuery = request.query as Record<string, unknown>;

    // --- action=balances: edit or reset leave balance ---
    if (postQuery.action === "balances") {
      if (!authData.permissions.includes("attendance.balances")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }

      const balParsed = parseBody(AttendanceBalancesSchema, rawBody);
      if ("error" in balParsed) return error(reply, balParsed.error, 400);
      const balBody = balParsed.data;

      const result = await attendanceService.handleBalances({
        user_id: balBody.user_id,
        year: balBody.year,
        action_type: balBody.action_type,
        vacation_total: balBody.vacation_total,
        vacation_used: balBody.vacation_used,
        sick_used: balBody.sick_used,
      });

      if ("error" in result) return error(reply, result.error!, 400);

      await logAudit({
        request,
        authData,
        action: "update",
        entityType: "leave_balance",
        entityId: balBody.user_id,
        description: result.message.includes("resetována")
          ? `Resetována bilance pro rok ${result.year}`
          : `Upravena bilance dovolené pro rok ${result.year}`,
      });
      return success(reply, null, 200, result.message);
    }

    // --- action=bulk_attendance: bulk fill month ---
    if (postQuery.action === "bulk_attendance") {
      if (!authData.permissions.includes("attendance.manage")) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }

      const bulkParsed = parseBody(AttendanceBulkSchema, rawBody);
      if ("error" in bulkParsed) return error(reply, bulkParsed.error, 400);
      const bulkBody = bulkParsed.data;

      const result = await attendanceService.bulkCreateAttendance({
        month: bulkBody.month,
        user_ids: bulkBody.user_ids,
        arrival_time: bulkBody.arrival_time,
        departure_time: bulkBody.departure_time,
        break_start_time: bulkBody.break_start_time,
        break_end_time: bulkBody.break_end_time,
      });

      await logAudit({
        request,
        authData,
        action: "create",
        entityType: "attendance",
        entityId: 0,
        description: `Hromadně vytvořeno ${result.inserted} záznamů docházky pro ${bulkBody.month}`,
      });

      return success(
        reply,
        { inserted: result.inserted, skipped: result.skipped },
        200,
        result.message,
      );
    }

    // --- action=leave: add leave record directly ---
    if (postQuery.action === "leave") {
      const leaveParsed = parseBody(AttendanceLeaveSchema, rawBody);
      if ("error" in leaveParsed) return error(reply, leaveParsed.error, 400);
      const leaveBody = leaveParsed.data;

      if (
        leaveBody.user_id != null &&
        leaveBody.user_id !== authData.userId &&
        !authData.permissions.includes("attendance.manage")
      ) {
        return error(reply, "Nedostatečná oprávnění", 403);
      }

      const result = await attendanceService.createLeave(
        {
          user_id: leaveBody.user_id,
          date_from: leaveBody.date_from,
          date_to: leaveBody.date_to,
          leave_type: leaveBody.leave_type,
          leave_hours: leaveBody.leave_hours,
          notes: leaveBody.notes ?? undefined,
        },
        authData.userId,
      );

      if ("error" in result) return error(reply, result.error!, 400);
      return success(reply, { created: result.created }, 200, result.message!);
    }

    // Punch action (arrival / departure / break_start) from Dashboard or Attendance page
    if (rawBody.punch_action) {
      const punchParsed = parseBody(AttendancePunchSchema, rawBody);
      if ("error" in punchParsed) return error(reply, punchParsed.error, 400);
      const punchBody = punchParsed.data;

      const result = await attendanceService.punchAction(authData.userId, {
        punch_action: punchBody.punch_action,
        latitude: punchBody.latitude,
        longitude: punchBody.longitude,
        accuracy: punchBody.accuracy,
        address: punchBody.address,
      });

      if ("error" in result) return error(reply, result.error!, 400);

      await logAudit({
        request,
        authData,
        action: result.auditAction,
        entityType: "attendance",
        entityId: result.id,
        description: result.auditDescription,
      });
      return success(reply, { id: result.id }, result.status, result.message);
    }

    // Standard attendance record creation (from admin forms)
    const stdParsed = parseBody(CreateAttendanceSchema, rawBody);
    if ("error" in stdParsed) return error(reply, stdParsed.error, 400);
    const body = stdParsed.data;

    if (
      body.user_id != null &&
      body.user_id !== authData.userId &&
      !authData.permissions.includes("attendance.manage")
    ) {
      return error(reply, "Nedostatečná oprávnění", 403);
    }

    const result = await attendanceService.createAttendance(
      {
        user_id: body.user_id,
        shift_date: body.shift_date,
        arrival_time: body.arrival_time,
        arrival_lat: body.arrival_lat,
        arrival_lng: body.arrival_lng,
        arrival_accuracy: body.arrival_accuracy,
        arrival_address: body.arrival_address,
        departure_time: body.departure_time,
        departure_lat: body.departure_lat,
        departure_lng: body.departure_lng,
        departure_accuracy: body.departure_accuracy,
        departure_address: body.departure_address,
        break_start: body.break_start,
        break_end: body.break_end,
        notes: body.notes,
        project_id: body.project_id,
        leave_type: body.leave_type,
        leave_hours: body.leave_hours,
        project_logs: body.project_logs,
      },
      authData.userId,
    );
    if ("error" in result)
      return error(reply, result.error!, result.status ?? 400);

    await logAudit({
      request,
      authData,
      action: "create",
      entityType: "attendance",
      entityId: result.id,
      description: `Vytvořen záznam docházky`,
    });

    return success(reply, { id: result.id }, 201, "Záznam byl vytvořen");
  });

  // PUT /api/admin/attendance/:id
  fastify.put<{ Params: { id: string } }>(
    "/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;
      const parsed = parseBody(UpdateAttendanceSchema, request.body);
      if ("error" in parsed) return error(reply, parsed.error, 400);
      const body = parsed.data;

      const authData = request.authData!;
      const isAdmin = authData.permissions.includes("attendance.manage");

      const result = await attendanceService.updateAttendance(
        id,
        {
          arrival_time: body.arrival_time,
          departure_time: body.departure_time,
          break_start: body.break_start,
          break_end: body.break_end,
          notes: body.notes ?? undefined,
          project_id:
            body.project_id != null
              ? Number(body.project_id)
              : (body.project_id as number | null | undefined),
          leave_type: body.leave_type,
          leave_hours:
            body.leave_hours != null
              ? Number(body.leave_hours)
              : (body.leave_hours as number | null | undefined),
          project_logs: body.project_logs,
        },
        authData.userId,
        isAdmin,
      );

      if ("error" in result) return error(reply, result.error!, result.status!);

      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "attendance",
        entityId: id,
        description: `Upraven záznam docházky`,
      });

      return success(reply, { id }, 200, "Záznam byl aktualizován");
    },
  );

  // DELETE /api/admin/attendance/:id
  fastify.delete<{ Params: { id: string } }>(
    "/:id",
    { preHandler: requirePermission("attendance.manage") },
    async (request, reply) => {
      const id = parseId(request.params.id, reply);
      if (id === null) return;

      const result = await attendanceService.deleteAttendance(id);
      if ("error" in result) return error(reply, result.error!, 404);

      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "attendance",
        entityId: id,
        description: `Smazán záznam docházky`,
      });

      return success(reply, null, 200, "Záznam smazán");
    },
  );
}
