import { z } from 'zod';
import { TRPCError } from '@trpc/server';
import { eq, and } from 'drizzle-orm';
import { protectedProcedure, router } from "../_core/trpc.ts";
import {
  adminOnboardingProgress,
  adminProfiles,
  adminDeviceRegistrations,
  onboardingAnalytics,
} from "../../drizzle/schema.ts";
import { getDb } from "../db.ts";

const adminProcedure = protectedProcedure.use(({ ctx, next }) => {
  if (ctx.user.role !== 'admin' && ctx.user.role !== 'super_admin') {
    throw new TRPCError({ code: 'FORBIDDEN', message: 'Admin access required' });
  }
  return next({ ctx });
});

export const adminOnboardingRouter = router({
  // Get onboarding status for current admin
  getStatus: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    try {
      const progress = await db.query.adminOnboardingProgress.findFirst({
        where: eq(adminOnboardingProgress.adminId, ctx.user.id),
      });

      if (!progress) {
        return {
          isCompleted: false,
          currentStep: 'welcome',
          completedSteps: [],
          onboardingEnabled: false,
        };
      }

      return {
        isCompleted: progress.completedAt !== null,
        currentStep: progress.currentStep,
        completedSteps: Array.isArray(progress.completedSteps) ? progress.completedSteps : [],
        onboardingEnabled: progress.onboardingEnabled === 1,
      };
    } catch (error: any) {
      // Table doesn't exist yet - return default onboarding status
      if (error.message?.includes('admin_onboarding_progress')) {
        return {
          isCompleted: false,
          currentStep: 'welcome',
          completedSteps: [],
          onboardingEnabled: false,
        };
      }
      throw error;
    }
  }),

  // Get onboarding progress for current admin
  getProgress: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    try {
      const progress = await db.query.adminOnboardingProgress.findFirst({
        where: eq(adminOnboardingProgress.adminId, ctx.user.id),
      });

      if (!progress) {
        // Create initial progress record
        const [newProgress] = await db
          .insert(adminOnboardingProgress)
          .values({
            adminId: ctx.user.id,
            currentStep: 'welcome',
            completedSteps: [],
            permissionsAcknowledged: 0,
            securityTrainingCompleted: 0,
            deviceRegistered: 0,
          })
          .returning();

        return newProgress;
      }

      return progress;
    } catch (error: any) {
      // Table doesn't exist yet - return default progress
      if (error.message?.includes('admin_onboarding_progress')) {
        return {
          adminId: ctx.user.id,
          currentStep: 'welcome',
          completedSteps: [],
          permissionsAcknowledged: 0,
          securityTrainingCompleted: 0,
          deviceRegistered: 0,
        };
      }
      throw error;
    }
  }),

  // Update onboarding step
  updateStep: protectedProcedure
    .input(
      z.object({
        step: z.enum([
          'welcome',
          'role_assignment',
          'permission_training',
          'security_training',
          'device_registration',
          'completion',
        ]),
        data: z.record(z.any()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
      
      const progress = await db.query.adminOnboardingProgress.findFirst({
        where: eq(adminOnboardingProgress.adminId, ctx.user.id),
      });

      if (!progress) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Onboarding progress not found',
        });
      }

      const completedSteps = Array.isArray(progress.completedSteps)
        ? progress.completedSteps
        : [];

      if (!completedSteps.includes(input.step)) {
        completedSteps.push(input.step);
      }

      const updateData: any = {
        currentStep: input.step,
        completedSteps,
        lastActivityAt: new Date(),
      };

      // Handle step-specific data
      if (input.data) {
        if (input.step === 'role_assignment' && input.data.role) {
          updateData.roleAssigned = input.data.role;
        }
        if (input.step === 'permission_training' && input.data.acknowledged) {
          updateData.permissionsAcknowledged = 1;
        }
        if (input.step === 'security_training' && input.data.quizScore !== undefined) {
          updateData.securityTrainingCompleted = 1;
          updateData.securityQuizScore = input.data.quizScore;
        }
        if (input.step === 'device_registration' && input.data.device) {
          updateData.deviceRegistered = 1;
          updateData.deviceName = input.data.device.name;
          updateData.deviceBrowser = input.data.device.browser;
          updateData.deviceOs = input.data.device.os;
          updateData.deviceIpAddress = input.data.device.ipAddress;
        }
        if (input.step === 'completion') {
          updateData.completedAt = new Date();
        }
      }

      const [updated] = await db
        .update(adminOnboardingProgress)
        .set(updateData)
        .where(eq(adminOnboardingProgress.adminId, ctx.user.id))
        .returning();

      return updated;
    }),

  // Assign role to admin
  assignRole: adminProcedure
    .input(
      z.object({
        adminId: z.number(),
        role: z.enum([
          'super_admin',
          'admin',
          'moderator',
          'finance_team',
          'developer',
          'support_lead',
        ]),
      })
    )
    .mutation(async ({ input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
      
      const [updated] = await db
        .update(adminOnboardingProgress)
        .set({
          roleAssigned: input.role,
          lastActivityAt: new Date(),
        })
        .where(eq(adminOnboardingProgress.adminId, input.adminId))
        .returning();

      return updated;
    }),

  // Register device
  registerDevice: protectedProcedure
    .input(
      z.object({
        deviceName: z.string(),
        browser: z.string(),
        os: z.string(),
        ipAddress: z.string(),
        userAgent: z.string().optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
      
      // Generate device ID
      const deviceId = `${ctx.user.id}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

      const [device] = await db
        .insert(adminDeviceRegistrations)
        .values({
          adminId: ctx.user.id,
          deviceId,
          deviceName: input.deviceName,
          browser: input.browser,
          os: input.os,
          ipAddress: input.ipAddress,
          userAgent: input.userAgent,
          isActive: 1,
        })
        .returning();

      // Update onboarding progress
      await db
        .update(adminOnboardingProgress)
        .set({
          deviceRegistered: 1,
          deviceName: input.deviceName,
          deviceBrowser: input.browser,
          deviceOs: input.os,
          deviceIpAddress: input.ipAddress,
          lastActivityAt: new Date(),
        })
        .where(eq(adminOnboardingProgress.adminId, ctx.user.id));

      return device;
    }),

  // Get registered devices
  getDevices: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    const devices = await db.query.adminDeviceRegistrations.findMany({
      where: eq(adminDeviceRegistrations.adminId, ctx.user.id),
    });

    return devices;
  }),

  // Deactivate device
  deactivateDevice: protectedProcedure
    .input(z.object({ deviceId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
      
      const device = await db.query.adminDeviceRegistrations.findFirst({
        where: and(
          eq(adminDeviceRegistrations.deviceId, input.deviceId),
          eq(adminDeviceRegistrations.adminId, ctx.user.id)
        ),
      });

      if (!device) {
        throw new TRPCError({
          code: 'NOT_FOUND',
          message: 'Device not found',
        });
      }

      const [updated] = await db
        .update(adminDeviceRegistrations)
        .set({ isActive: 0 })
        .where(eq(adminDeviceRegistrations.deviceId, input.deviceId))
        .returning();

      return updated;
    }),

  // Create or update admin profile
  saveProfile: protectedProcedure
    .input(
      z.object({
        avatarUrl: z.string().optional(),
        phoneNumber: z.string().optional(),
        timezone: z.string().default('UTC'),
        language: z.string().default('en'),
        notificationPreferences: z.record(z.any()).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
      
      const existing = await db.query.adminProfiles.findFirst({
        where: eq(adminProfiles.adminId, ctx.user.id),
      });

      if (existing) {
        const [updated] = await db
          .update(adminProfiles)
          .set({
            ...input,
            lastProfileUpdateAt: new Date(),
          })
          .where(eq(adminProfiles.adminId, ctx.user.id))
          .returning();

        return updated;
      } else {
        const [created] = await db
          .insert(adminProfiles)
          .values({
            adminId: ctx.user.id,
            ...input,
          })
          .returning();

        return created;
      }
    }),

  // Get admin profile
  getProfile: protectedProcedure.query(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    const profile = await db.query.adminProfiles.findFirst({
      where: eq(adminProfiles.adminId, ctx.user.id),
    });

    if (!profile) {
      // Create default profile
      const [newProfile] = await db
        .insert(adminProfiles)
        .values({
          adminId: ctx.user.id,
          timezone: 'UTC',
          language: 'en',
        })
        .returning();

      return newProfile;
    }

    return profile;
  }),

  // Get onboarding analytics (admin only)
  getAnalytics: adminProcedure.query(async () => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    const today = new Date().toISOString().split('T')[0];

    const analytics = await db.query.onboardingAnalytics.findFirst({
      where: eq(onboardingAnalytics.dateRecorded, today),
    });

    if (!analytics) {
      // Calculate fresh analytics
      const allProgress = await db.query.adminOnboardingProgress.findMany();

      const completed = allProgress.filter((p) => p.completedAt !== null).length;
      const pending = allProgress.length - completed;

      const avgTime =
        completed > 0
          ? Math.round(
              allProgress
                .filter((p) => p.completedAt)
                .reduce((sum, p) => {
                  const start = new Date(p.startedAt).getTime();
                  const end = new Date(p.completedAt!).getTime();
                  return sum + (end - start) / (1000 * 60);
                }, 0) / completed
            )
          : null;

      const avgScore =
        allProgress.filter((p) => p.securityQuizScore).length > 0
          ? (
              allProgress
                .filter((p) => p.securityQuizScore)
                .reduce((sum, p) => sum + (p.securityQuizScore || 0), 0) /
              allProgress.filter((p) => p.securityQuizScore).length
            ).toFixed(2)
          : null;

      const roleDistribution = allProgress.reduce(
        (acc, p) => {
          if (p.roleAssigned) {
            acc[p.roleAssigned] = (acc[p.roleAssigned] || 0) + 1;
          }
          return acc;
        },
        {} as Record<string, number>
      );

      const [newAnalytics] = await db
        .insert(onboardingAnalytics)
        .values({
          totalAdmins: allProgress.length,
          completedOnboardings: completed,
          pendingOnboardings: pending,
          averageCompletionTimeMinutes: avgTime,
          averageQuizScore: avgScore ? parseFloat(avgScore) : null,
          stepCompletionRates: {},
          roleDistribution,
          dateRecorded: today,
        })
        .returning();

      return newAnalytics;
    }

    return analytics;
  }),

  // Complete onboarding
  completeOnboarding: protectedProcedure.mutation(async ({ ctx }) => {
    const db = await getDb();
    if (!db) throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Database connection failed' });
    
    try {
      console.log(`[Onboarding] Completing onboarding for admin ${ctx.user.id}`);
      
      const [updated] = await db
        .update(adminOnboardingProgress)
        .set({
          currentStep: 'completion',
          completedAt: new Date(),
          lastActivityAt: new Date(),
        })
        .where(eq(adminOnboardingProgress.adminId, ctx.user.id))
        .returning();

      if (!updated) {
        console.error(`[Onboarding] Failed to update onboarding for admin ${ctx.user.id} - no record returned`);
        throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to mark onboarding as complete' });
      }

      console.log(`[Onboarding] Successfully completed onboarding for admin ${ctx.user.id}`, updated);
      return updated;
    } catch (error: any) {
      console.error(`[Onboarding] Error completing onboarding for admin ${ctx.user.id}:`, error);
      throw error;
    }
  }),
});
