import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { prisma } from '@shieldai/db';

// In-memory rate limiter for device registration
const registrationAttempts = new Map<string, { count: number; resetAt: number }>();
const REGISTRATION_RATE_LIMIT = 10; // max registrations per window
const REGISTRATION_WINDOW_MS = 5 * 60 * 1000; // 5 minutes

function checkRegistrationRateLimit(key: string): boolean {
  const now = Date.now();
  const record = registrationAttempts.get(key);

  if (!record || now > record.resetAt) {
    registrationAttempts.set(key, { count: 1, resetAt: now + REGISTRATION_WINDOW_MS });
    return true;
  }

  if (record.count >= REGISTRATION_RATE_LIMIT) {
    return false;
  }

  record.count++;
  return true;
}

// Cleanup stale rate limit entries every 5 minutes
setInterval(() => {
  const now = Date.now();
  for (const [key, record] of registrationAttempts.entries()) {
    if (now > record.resetAt) {
      registrationAttempts.delete(key);
    }
  }
}, REGISTRATION_WINDOW_MS);

export async function deviceRoutes(fastify: FastifyInstance) {
  // Register device for push notifications
  fastify.post(
    '/devices/register',
    {
      preHandler: async (request, reply) => {
        const authReq = request as FastifyRequest & { user?: { id: string } };
        const userId = authReq.user?.id;
        if (!userId) {
          return reply.status(401).send({ error: 'Authentication required' });
        }
      },
    },
    async (request, reply) => {
      const authReq = request as FastifyRequest & { user?: { id: string } };
      const { platform, fcmToken, apnsToken, appVersion, osVersion, deviceModel } = request.body as {
        platform: 'ios' | 'android';
        fcmToken?: string;
        apnsToken?: string;
        appVersion: string;
        osVersion: string;
        deviceModel?: string;
      };

      if (!authReq.user?.id) {
        return reply.status(401).send({ error: 'Authentication required' });
      }

      if (!platform || !appVersion || !osVersion) {
        return reply.status(400).send({
          error: 'Missing required fields',
          required: ['platform', 'appVersion', 'osVersion'],
        });
      }

      // Rate limit registration per user
      if (!checkRegistrationRateLimit(authReq.user.id)) {
        return reply.status(429).send({
          error: 'Too many registration attempts',
          retryAfter: Math.ceil(REGISTRATION_WINDOW_MS / 1000),
        });
      }

      if (platform === 'android' && !fcmToken) {
        return reply.status(400).send({
          error: 'FCM token required for Android devices',
        });
      }

      if (platform === 'ios' && !apnsToken) {
        return reply.status(400).send({
          error: 'APNs token required for iOS devices',
        });
      }

      try {
        // Determine device type based on platform
        const deviceType = platform === 'ios' || platform === 'android' ? 'mobile' : 'web';
        
        // Determine the token to store
        const deviceToken = platform === 'ios' ? apnsToken! : fcmToken!;

        // Upsert device registration to handle token rotation and re-registration
        const deviceRegistration = await prisma.deviceToken.upsert({
          where: { token: deviceToken },
          create: {
            userId: authReq.user.id,
            deviceType,
            token: deviceToken,
            platform,
            appVersion,
            osVersion,
            model: deviceModel,
            isActive: true,
            lastUsedAt: new Date(),
          },
          update: {
            userId: authReq.user.id,
            deviceType,
            platform,
            appVersion,
            osVersion,
            model: deviceModel,
            isActive: true,
            lastUsedAt: new Date(),
          },
        });

        return {
          success: true,
          device: {
            deviceId: deviceRegistration.id,
            platform: deviceRegistration.platform,
            registeredAt: deviceRegistration.createdAt.toISOString(),
          },
          message: 'Device registered successfully',
        };
      } catch (error) {
        console.error('Failed to register device:', error);
        reply.status(500).send({
          error: 'Failed to register device',
          message: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }
  );

  // Update device push tokens
  fastify.put(
    '/devices/:deviceId/tokens',
    {
      preHandler: async (request, reply) => {
        const authReq = request as FastifyRequest & { user?: { id: string } };
        const userId = authReq.user?.id;
        if (!userId) {
          return reply.status(401).send({ error: 'Authentication required' });
        }
      },
    },
    async (request, reply) => {
      const authReq = request as FastifyRequest & { user?: { id: string } };
      const { deviceId } = request.params as { deviceId: string };
      const { fcmToken, apnsToken, deviceModel } = request.body as {
        fcmToken?: string;
        apnsToken?: string;
        deviceModel?: string;
      };

      if (!authReq.user?.id) {
        return reply.status(401).send({ error: 'Authentication required' });
      }

      if (!fcmToken && !apnsToken) {
        return reply.status(400).send({
          error: 'At least one token is required',
        });
      }

      try {
        // Find the device first
        const existingDevice = await prisma.deviceToken.findFirst({
          where: {
            id: deviceId,
            userId: authReq.user.id,
            isActive: true,
          },
        });

        if (!existingDevice) {
          return reply.status(404).send({
            error: 'Device not found',
          });
        }

        // Determine which token to update based on platform
        const updateData: {
          token: string;
          appVersion?: string;
          osVersion?: string;
          model?: string;
          lastUsedAt: Date;
        } = {
          token: existingDevice.platform === 'ios' ? (apnsToken || existingDevice.token) : (fcmToken || existingDevice.token),
          lastUsedAt: new Date(),
        };

        if (deviceModel) {
          updateData.model = deviceModel;
        }

        // Update device tokens in database
        const device = await prisma.deviceToken.update({
          where: { 
            id: deviceId,
            userId: authReq.user.id,
          },
          data: updateData,
        });

        return {
          success: true,
          device: {
            deviceId: device.id,
            platform: device.platform,
            lastActiveAt: device.lastUsedAt.toISOString(),
          },
          message: 'Device tokens updated successfully',
        };
      } catch (error) {
        console.error('Failed to update device tokens:', error);
        reply.status(500).send({
          error: 'Failed to update device tokens',
          message: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }
  );

  // Get user's registered devices
  fastify.get(
    '/devices',
    {
      preHandler: async (request, reply) => {
        const authReq = request as FastifyRequest & { user?: { id: string } };
        const userId = authReq.user?.id;
        if (!userId) {
          return reply.status(401).send({ error: 'Authentication required' });
        }
      },
    },
    async (request, reply) => {
      const authReq = request as FastifyRequest & { user?: { id: string } };

      if (!authReq.user?.id) {
        return reply.status(401).send({ error: 'Authentication required' });
      }

      try {
        // Fetch devices from database
        const devices = await prisma.deviceToken.findMany({
          where: { 
            userId: authReq.user.id,
            isActive: true,
          },
          select: {
            id: true,
            platform: true,
            appVersion: true,
            osVersion: true,
            model: true,
            lastUsedAt: true,
            createdAt: true,
          },
          orderBy: {
            lastUsedAt: 'desc',
          },
        });

        return {
          success: true,
          devices: devices.map((d) => ({
            deviceId: d.id,
            platform: d.platform,
            appVersion: d.appVersion,
            osVersion: d.osVersion,
            model: d.model,
            lastActiveAt: d.lastUsedAt.toISOString(),
            registeredAt: d.createdAt.toISOString(),
          })),
          message: 'Device list retrieved',
        };
      } catch (error) {
        console.error('Failed to fetch devices:', error);
        reply.status(500).send({
          error: 'Failed to fetch devices',
          message: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }
  );

  // Deregister device
  fastify.delete(
    '/devices/:deviceId',
    {
      preHandler: async (request, reply) => {
        const authReq = request as FastifyRequest & { user?: { id: string } };
        const userId = authReq.user?.id;
        if (!userId) {
          return reply.status(401).send({ error: 'Authentication required' });
        }
      },
    },
    async (request, reply) => {
      const authReq = request as FastifyRequest & { user?: { id: string } };
      const { deviceId } = request.params as { deviceId: string };

      if (!authReq.user?.id) {
        return reply.status(401).send({ error: 'Authentication required' });
      }

      try {
        // Soft delete by marking as inactive
        await prisma.deviceToken.update({
          where: { 
            id: deviceId,
            userId: authReq.user.id,
          },
          data: {
            isActive: false,
          },
        });

        return {
          success: true,
          message: 'Device deregistered successfully',
        };
      } catch (error) {
        console.error('Failed to deregister device:', error);
        reply.status(500).send({
          error: 'Failed to deregister device',
          message: error instanceof Error ? error.message : 'Unknown error',
        });
      }
    }
  );
}
