import { protectedProcedure, publicProcedure, router } from "../_core/trpc.ts";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { getDb, getTickets, getTicketMessages, writeAuditLog, getUserById } from "../db.ts";
import { eq, and, isNull, desc } from "drizzle-orm";
import { staff, timeClock, supportTickets, ticketMessages, users } from "../../drizzle/schema.ts";
import crypto from "crypto";

function hashPin(pin: string): string {
  return crypto.createHash("sha256").update(pin + "coinkrazy-salt").digest("hex");
}

const staffProcedure = protectedProcedure.use(({ ctx, next }) => {
  if (ctx.user.role !== "staff" && ctx.user.role !== "admin") {
    throw new TRPCError({ code: "FORBIDDEN", message: "Staff access required" });
  }
  return next({ ctx });
});

export const staffPanelRouter = router({
  // PIN Authentication
  verifyPin: protectedProcedure
    .input(z.object({ pin: z.string().length(4) }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
      if (!staffRecord[0]) throw new TRPCError({ code: "FORBIDDEN", message: "Not a staff member" });
      const hashed = hashPin(input.pin);
      if (staffRecord[0].pin !== hashed) throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid PIN" });
      return { success: true, staffId: staffRecord[0].staffId, position: staffRecord[0].position };
    }),

  getMyProfile: staffProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("DB unavailable");
    const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
    return staffRecord[0];
  }),

  // Time Clock
  clockIn: staffProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("DB unavailable");
    const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
    if (!staffRecord[0]) throw new Error("Staff record not found");
    // Check not already clocked in
    const active = await db.select().from(timeClock).where(and(eq(timeClock.staffId, staffRecord[0].id), isNull(timeClock.clockOut))).limit(1);
    if (active[0]) throw new Error("Already clocked in");
    await db.insert(timeClock).values({ staffId: staffRecord[0].id, clockIn: new Date() });
    await writeAuditLog({ actorId: ctx.user.id, actorRole: "staff", action: "clock_in", category: "staff" });
    return { success: true, clockIn: new Date() };
  }),

  clockOut: staffProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new Error("DB unavailable");
    const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
    if (!staffRecord[0]) throw new Error("Staff record not found");
    const active = await db.select().from(timeClock).where(and(eq(timeClock.staffId, staffRecord[0].id), isNull(timeClock.clockOut))).limit(1);
    if (!active[0]) throw new Error("Not clocked in");
    const clockOut = new Date();
    const hoursWorked = (clockOut.getTime() - new Date(active[0].clockIn).getTime()) / 3600000;
    await db.update(timeClock).set({ clockOut, hoursWorked }).where(eq(timeClock.id, active[0].id));
    await writeAuditLog({ actorId: ctx.user.id, actorRole: "staff", action: "clock_out", category: "staff", details: { hoursWorked } });
    return { success: true, clockOut, hoursWorked };
  }),

  getTimeLog: staffProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) return [];
    const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
    if (!staffRecord[0]) return [];
    return db.select().from(timeClock).where(eq(timeClock.staffId, staffRecord[0].id)).orderBy(desc(timeClock.clockIn)).limit(30);
  }),

  getPayrollSummary: staffProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) return null;
    const staffRecord = await db.select().from(staff).where(eq(staff.userId, ctx.user.id)).limit(1);
    if (!staffRecord[0]) return null;
    const logs = await db.select().from(timeClock).where(eq(timeClock.staffId, staffRecord[0].id));
    const totalHours = logs.reduce((sum, l) => sum + (l.hoursWorked ?? 0), 0);
    const hourlyRate = parseFloat(staffRecord[0].hourlyRate ?? "0");
    return { totalHours, hourlyRate, estimatedPay: totalHours * hourlyRate, staffId: staffRecord[0].staffId, position: staffRecord[0].position };
  }),

  // Support Tickets
  getTickets: staffProcedure
    .input(z.object({ status: z.string().optional(), limit: z.number().default(20) }))
    .query(async ({ input }) => {
      return getTickets(undefined, input.status, input.limit);
    }),

  getTicketDetail: staffProcedure
    .input(z.object({ ticketId: z.number() }))
    .query(async ({ input }) => {
      const db = await getDb();
      if (!db) return null;
      const ticket = await db.select().from(supportTickets).where(eq(supportTickets.id, input.ticketId)).limit(1);
      const messages = await getTicketMessages(input.ticketId);
      return { ticket: ticket[0], messages };
    }),

  replyToTicket: staffProcedure
    .input(z.object({ ticketId: z.number(), message: z.string().min(1) }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      await db.insert(ticketMessages).values({ ticketId: input.ticketId, senderId: ctx.user.id, senderRole: "staff", message: input.message });
      await db.update(supportTickets).set({ status: "in_progress", updatedAt: new Date() }).where(eq(supportTickets.id, input.ticketId));
      return { success: true };
    }),

  resolveTicket: staffProcedure
    .input(z.object({ ticketId: z.number() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      await db.update(supportTickets).set({ status: "resolved", resolvedAt: new Date(), assignedStaffId: ctx.user.id }).where(eq(supportTickets.id, input.ticketId));
      await writeAuditLog({ actorId: ctx.user.id, actorRole: "staff", action: "ticket_resolved", category: "staff", details: { ticketId: input.ticketId } });
      return { success: true };
    }),

  lookupPlayer: staffProcedure
    .input(z.object({ userId: z.number() }))
    .query(async ({ input }) => {
      const user = await getUserById(input.userId);
      if (!user) throw new TRPCError({ code: "NOT_FOUND" });
      const db = await getDb();
      if (!db) return { user };
      const { wallets } = await import("../../drizzle/schema");
      const wallet = await db.select().from(wallets).where(eq(wallets.userId, input.userId)).limit(1);
      return { user, wallet: wallet[0] };
    }),

  // User support ticket creation (for players)
  createTicket: protectedProcedure
    .input(z.object({
      subject: z.string().min(1).max(255),
      category: z.enum(["account", "payment", "game", "bonus", "technical", "kyc", "other"]),
      message: z.string().min(1),
      priority: z.enum(["low", "medium", "high", "urgent"]).default("medium"),
    }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("DB unavailable");
      await db.insert(supportTickets).values({ userId: ctx.user.id, subject: input.subject, category: input.category, priority: input.priority, status: "open" });
      const ticket = await db.select().from(supportTickets).where(eq(supportTickets.userId, ctx.user.id)).orderBy(desc(supportTickets.createdAt)).limit(1);
      if (ticket[0]) {
        await db.insert(ticketMessages).values({ ticketId: ticket[0].id, senderId: ctx.user.id, senderRole: "user", message: input.message });
      }
      return { success: true, ticketId: ticket[0]?.id };
    }),

  getUserTickets: protectedProcedure.query(async ({ ctx }) => {
    return getTickets(ctx.user.id);
  }),
});
