import { router, protectedProcedure } from "../_core/trpc.ts";
import { z } from "zod";
import { getDb } from "../db.ts";
import { adminShifts, adminShiftAssignments } from "../../drizzle/schema.ts";
import { eq } from "drizzle-orm";

// Shift template types
interface ShiftTemplate {
  id: string;
  name: string;
  description: string;
  shifts: Array<{
    name: string;
    startTime: string;
    endTime: string;
    requiredCount: number;
  }>;
  recurrence: "daily" | "weekly" | "biweekly" | "monthly";
  createdAt: Date;
  updatedAt: Date;
}

// In-memory storage for templates (in production, use database)
const shiftTemplates = new Map<string, ShiftTemplate>();

export const adminBulkShiftsRouter = router({
  // Create shift template
  createShiftTemplate: protectedProcedure
    .input(
      z.object({
        name: z.string(),
        description: z.string().optional(),
        shifts: z.array(
          z.object({
            name: z.string(),
            startTime: z.string(),
            endTime: z.string(),
            requiredCount: z.number().min(1),
          })
        ),
        recurrence: z.enum(["daily", "weekly", "biweekly", "monthly"]),
      })
    )
    .mutation(async ({ input }) => {
      const db = await getDb();
      const templateId = `template-${Date.now()}`;
      const template: ShiftTemplate = {
        id: templateId,
        name: input.name,
        description: input.description,
        shifts: input.shifts,
        recurrence: input.recurrence,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      shiftTemplates.set(templateId, template);
      return { success: true, templateId };
    }),

  // Get all shift templates
  getShiftTemplates: protectedProcedure.query(async () => {
    const db = await getDb();
    return Array.from(shiftTemplates.values());
  }),

  // Get shift template by ID
  getShiftTemplate: protectedProcedure
    .input(z.object({ templateId: z.string() }))
    .query(async ({ input }) => {
      const db = await getDb();
      return shiftTemplates.get(input.templateId) || null;
    }),

  // Update shift template
  updateShiftTemplate: protectedProcedure
    .input(
      z.object({
        templateId: z.string(),
        name: z.string().optional(),
        description: z.string().optional(),
        shifts: z
          .array(
            z.object({
              name: z.string(),
              startTime: z.string(),
              endTime: z.string(),
              requiredCount: z.number().min(1),
            })
          )
          .optional(),
        recurrence: z.enum(["daily", "weekly", "biweekly", "monthly"]).optional(),
      })
    )
    .mutation(async ({ input }) => {
      const db = await getDb();
      const template = shiftTemplates.get(input.templateId);
      if (!template) return { success: false };

      const updated: ShiftTemplate = {
        ...template,
        name: input.name || template.name,
        description: input.description || template.description,
        shifts: input.shifts || template.shifts,
        recurrence: input.recurrence || template.recurrence,
        updatedAt: new Date(),
      };

      shiftTemplates.set(input.templateId, updated);
      return { success: true };
    }),

  // Delete shift template
  deleteShiftTemplate: protectedProcedure
    .input(z.object({ templateId: z.string() }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      shiftTemplates.delete(input.templateId);
      return { success: true };
    }),

  // Apply template to create shifts for a week
  applyTemplateToWeek: protectedProcedure
    .input(
      z.object({
        templateId: z.string(),
        startDate: z.date(),
        endDate: z.date(),
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();
      const template = shiftTemplates.get(input.templateId);
      if (!template) return { success: false, createdCount: 0 };

      const createdShifts: number[] = [];
      let currentDate = new Date(input.startDate);

      while (currentDate <= input.endDate) {
        for (const shiftTemplate of template.shifts) {
          const result = await db.insert(adminShifts).values({
            name: shiftTemplate.name,
            startTime: shiftTemplate.startTime,
            endTime: shiftTemplate.endTime,
            requiredCount: shiftTemplate.requiredCount,
            shiftDate: currentDate,
            createdBy: ctx.user?.id || 0,
          });

          createdShifts.push(result.insertId as number);
        }

        // Move to next day
        currentDate = new Date(currentDate.getTime() + 86400000);
      }

      return { success: true, createdCount: createdShifts.length };
    }),

  // Apply template for recurring pattern (e.g., every Monday)
  applyRecurringTemplate: protectedProcedure
    .input(
      z.object({
        templateId: z.string(),
        startDate: z.date(),
        weeks: z.number().min(1).max(52),
        daysOfWeek: z.array(z.number().min(0).max(6)), // 0 = Sunday, 6 = Saturday
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();
      const template = shiftTemplates.get(input.templateId);
      if (!template) return { success: false, createdCount: 0 };

      const createdShifts: number[] = [];
      let currentDate = new Date(input.startDate);
      let weeksProcessed = 0;

      while (weeksProcessed < input.weeks) {
        const dayOfWeek = currentDate.getDay();

        if (input.daysOfWeek.includes(dayOfWeek)) {
          for (const shiftTemplate of template.shifts) {
            const result = await db.insert(adminShifts).values({
              name: shiftTemplate.name,
              startTime: shiftTemplate.startTime,
              endTime: shiftTemplate.endTime,
              requiredCount: shiftTemplate.requiredCount,
              shiftDate: currentDate,
              createdBy: ctx.user?.id || 0,
            });

            createdShifts.push(result.insertId as number);
          }
        }

        // Move to next day
        currentDate = new Date(currentDate.getTime() + 86400000);

        // Check if we've moved to the next week
        if (currentDate.getDay() === 0) {
          weeksProcessed++;
        }
      }

      return { success: true, createdCount: createdShifts.length };
    }),

  // Bulk assign admins to shifts
  bulkAssignAdmins: protectedProcedure
    .input(
      z.object({
        shiftIds: z.array(z.number()),
        adminIds: z.array(z.number()),
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();
      let assignmentCount = 0;

      for (const shiftId of input.shiftIds) {
        for (const adminId of input.adminIds) {
          try {
            await db.insert(adminShiftAssignments).values({
              shiftId,
              adminId,
              assignedBy: ctx.user?.id || 0,
            });
            assignmentCount++;
          } catch (error) {
            // Skip if assignment already exists
            console.error(`Failed to assign admin ${adminId} to shift ${shiftId}:`, error);
          }
        }
      }

      return { success: true, assignmentCount };
    }),

  // Bulk remove admins from shifts
  bulkRemoveAdmins: protectedProcedure
    .input(
      z.object({
        shiftIds: z.array(z.number()),
        adminIds: z.array(z.number()),
      })
    )
    .mutation(async ({ input }) => {
      const db = await getDb();
      let removalCount = 0;

      for (const shiftId of input.shiftIds) {
        for (const adminId of input.adminIds) {
          const result = await db
            .delete(adminShiftAssignments)
            .where(
              eq(adminShiftAssignments.shiftId, shiftId) &&
              eq(adminShiftAssignments.adminId, adminId)
            );

          // Count deleted rows
          removalCount += result.rowsAffected || 0;
        }
      }

      return { success: true, removalCount };
    }),

  // Bulk delete shifts
  bulkDeleteShifts: protectedProcedure
    .input(z.object({ shiftIds: z.array(z.number()) }))
    .mutation(async ({ input }) => {
      const db = await getDb();
      let deletedCount = 0;

      for (const shiftId of input.shiftIds) {
        // Delete assignments first
        await db.delete(adminShiftAssignments).where(eq(adminShiftAssignments.shiftId, shiftId));

        // Then delete shift
        const result = await db.delete(adminShifts).where(eq(adminShifts.id, shiftId));

        deletedCount += result.rowsAffected || 0;
      }

      return { success: true, deletedCount };
    }),

  // Clone shifts from one week to another
  cloneShifts: protectedProcedure
    .input(
      z.object({
        fromDate: z.date(),
        toDate: z.date(),
        includeAssignments: z.boolean().default(false),
      })
    )
    .mutation(async ({ input, ctx }) => {
      const db = await getDb();
      // Get shifts from source week
      const sourceShifts = await db
        .select()
        .from(adminShifts)
        .where(eq(adminShifts.shiftDate, input.fromDate));

      let clonedCount = 0;

      for (const shift of sourceShifts) {
        const result = await db.insert(adminShifts).values({
          name: shift.name,
          startTime: shift.startTime,
          endTime: shift.endTime,
          requiredCount: shift.requiredCount,
          shiftDate: input.toDate,
          createdBy: ctx.user?.id || 0,
        });

        const newShiftId = result.insertId as number;

        // Clone assignments if requested
        if (input.includeAssignments) {
          const assignments = await db
            .select()
            .from(adminShiftAssignments)
            .where(eq(adminShiftAssignments.shiftId, shift.id));

          for (const assignment of assignments) {
            await db.insert(adminShiftAssignments).values({
              shiftId: newShiftId,
              adminId: assignment.adminId,
              assignedBy: ctx.user?.id || 0,
            });
          }
        }

        clonedCount++;
      }

      return { success: true, clonedCount };
    }),

  // Get shift statistics for a date range
  getShiftStatistics: protectedProcedure
    .input(
      z.object({
        startDate: z.date(),
        endDate: z.date(),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      const shifts = await db
        .select()
        .from(adminShifts)
        .where(
          eq(adminShifts.shiftDate, input.startDate) ||
          eq(adminShifts.shiftDate, input.endDate)
        );

      const totalShifts = shifts.length;
      const filledShifts = shifts.filter((s) => s.status === "filled").length;
      const openShifts = shifts.filter((s) => s.status === "open").length;
      const overfilledShifts = shifts.filter((s) => s.status === "overfilled").length;

      const totalRequiredAdmins = shifts.reduce((sum, s) => sum + s.requiredCount, 0);

      return {
        totalShifts,
        filledShifts,
        openShifts,
        overfilledShifts,
        totalRequiredAdmins,
        coveragePercentage: totalShifts > 0 ? Math.round((filledShifts / totalShifts) * 100) : 0,
      };
    }),

  // Export shifts to CSV format
  exportShifts: protectedProcedure
    .input(
      z.object({
        startDate: z.date(),
        endDate: z.date(),
      })
    )
    .query(async ({ input }) => {
      const db = await getDb();
      const shifts = await db
        .select()
        .from(adminShifts)
        .where(
          eq(adminShifts.shiftDate, input.startDate) ||
          eq(adminShifts.shiftDate, input.endDate)
        );

      // Format as CSV
      const headers = ["ID", "Name", "Date", "Start Time", "End Time", "Required Count", "Status"];
      const rows = shifts.map((s) => [
        s.id,
        s.name,
        s.shiftDate.toISOString().split("T")[0],
        s.startTime,
        s.endTime,
        s.requiredCount,
        s.status,
      ]);

      const csvContent = [
        headers.join(","),
        ...rows.map((r) => r.join(",")),
      ].join("\n");

      return {
        success: true,
        csv: csvContent,
        filename: `shifts-${input.startDate.toISOString().split("T")[0]}-to-${input.endDate.toISOString().split("T")[0]}.csv`,
      };
    }),
});
