import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { prisma, SubscriptionTier } from '@shieldai/db';
import { detectChanges, shouldTriggerAlert } from '@shieldai/hometitle';
import { homeTitleAlertPipeline } from '@shieldai/hometitle';
import { AuthRequest } from '../auth.middleware';

const HOMETITLE_PROPERTY_LIMITS: Record<string, number> = {
  free: 0,
  basic: 0,
  plus: 3,
  premium: 5,
};

const HOMETITLE_TIER_ORDER: Record<string, number> = {
  free: 0,
  basic: 1,
  plus: 2,
  premium: 3,
};

/**
 * Middleware: require Premium tier for home title features.
 * Returns subscriptionId if user is Premium, otherwise replies with 402.
 */
async function requirePremiumTier(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<string | null> {
  const authReq = request as AuthRequest;
  const userId = authReq.user?.id;

  if (!userId) {
    await reply.code(401).send({ error: 'User not authenticated' });
    return null;
  }

  const subscription = await prisma.subscription.findFirst({
    where: { userId, status: 'active' },
    select: { id: true, tier: true },
  });

  if (!subscription) {
    await reply.code(402).send({
      error: 'Subscription required',
      message: 'A home title monitoring subscription is required',
    });
    return null;
  }

  if (subscription.tier !== 'premium') {
    const currentTier = HOMETITLE_TIER_ORDER[subscription.tier] ?? 0;
    const nextTier = 'plus';

    await reply.code(402).send({
      error: 'Premium tier required',
      message: `Home title monitoring requires a Premium subscription. You are currently on ${subscription.tier}.`,
      currentTier: subscription.tier,
      upgradeTo: nextTier,
    });
    return null;
  }

  return subscription.id;
}

/**
 * Check property limit for the user's tier.
 * Returns true if under limit, replies 400 if exceeded.
 */
async function checkPropertyLimit(
  reply: FastifyReply,
  subscriptionId: string,
): Promise<boolean> {
  const subscription = await prisma.subscription.findUnique({
    where: { id: subscriptionId },
    select: { tier: true },
  });

  if (!subscription) {
    return false;
  }

  const limit = HOMETITLE_PROPERTY_LIMITS[subscription.tier] ?? 0;
  const count = await prisma.watchlistItem.count({
    where: { subscriptionId, type: 'address' },
  });

  if (count >= limit) {
    await reply.code(400).send({
      error: 'Property limit reached',
      message: `You have reached the maximum of ${limit} properties for your ${subscription.tier} tier.`,
      currentCount: count,
      limit,
      upgradeTo: 'premium',
    });
    return false;
  }

  return true;
}

export async function hometitleRoutes(fastify: FastifyInstance) {
  // GET /hometitle/properties - List monitored properties with status
  fastify.get('/properties', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    try {
      const watchlistItems = await prisma.watchlistItem.findMany({
        where: { subscriptionId, type: 'address', isActive: true },
        orderBy: { createdAt: 'desc' },
      });

      const itemIds = watchlistItems.map((item) => item.id);

      // Batch query: fetch all recent alerts in one query
      const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
      const allRecentAlerts = await prisma.alert.findMany({
        where: {
          subscriptionId,
          watchlistItemId: { in: itemIds },
          createdAt: { gte: sevenDaysAgo },
        },
        orderBy: { createdAt: 'desc' },
      });

      // Group alerts by watchlistItemId
      const alertsByItem = new Map<string, typeof allRecentAlerts>();
      for (const alert of allRecentAlerts) {
        const arr = alertsByItem.get(alert.watchlistItemId) || [];
        arr.push(alert);
        alertsByItem.set(alert.watchlistItemId, arr);
      }

      const properties = watchlistItems.map((item) => {
        const recentAlerts = (alertsByItem.get(item.id) || []).slice(0, 3);
        const hasRecentAlerts = recentAlerts.length > 0;
        const latestAlert = recentAlerts[0];

        let status: 'monitored' | 'alert' | 'error' = 'monitored';
        if (hasRecentAlerts && latestAlert) {
          status = latestAlert.severity === 'CRITICAL' ? 'alert' : 'monitored';
        }

        return {
          id: item.id,
          address: item.value,
          status,
          lastScan: null,
          lastChange: latestAlert ? latestAlert.createdAt : null,
          alertCount: recentAlerts.length,
          addedAt: item.createdAt,
        };
      });

      return reply.send({ properties });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to list properties';
      return reply.code(500).send({ error: message });
    }
  });

  // GET /hometitle/properties/stats - Dashboard widget stats (available to all active subscriptions)
  fastify.get('/properties/stats', async (request: FastifyRequest, reply: FastifyReply) => {
    const authReq = request as AuthRequest;
    const userId = authReq.user?.id;

    if (!userId) {
      return reply.code(401).send({ error: 'User not authenticated' });
    }

    const subscription = await prisma.subscription.findFirst({
      where: { userId, status: 'active' },
      select: { id: true, tier: true },
    });

    if (!subscription) {
      return reply.code(404).send({ error: 'Active subscription not found' });
    }

    try {
      const propertyCount = await prisma.watchlistItem.count({
        where: { subscriptionId: subscription.id, type: 'address', isActive: true },
      });

      const limit = HOMETITLE_PROPERTY_LIMITS[subscription.tier] ?? 0;
      const isPremium = subscription.tier === 'premium';

      // Count recent alerts (last 7 days)
      const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
      const recentAlertCount = await prisma.alert.count({
        where: {
          subscriptionId: subscription.id,
          createdAt: { gte: sevenDaysAgo },
        },
      });

      // Count critical alerts
      const criticalAlertCount = await prisma.alert.count({
        where: {
          subscriptionId: subscription.id,
          severity: 'CRITICAL',
          createdAt: { gte: sevenDaysAgo },
        },
      });

      return reply.send({
        monitoredProperties: propertyCount,
        tier: subscription.tier,
        isPremium,
        propertyLimit: limit,
        canAddMore: propertyCount < limit,
        recentAlertCount,
        criticalAlertCount,
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to fetch stats';
      return reply.code(500).send({ error: message });
    }
  });

  // POST /hometitle/properties - Add property to monitor
  fastify.post('/properties', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    const body = request.body as { address: string };

    if (!body.address || typeof body.address !== 'string') {
      return reply.code(400).send({
        error: 'Invalid request',
        message: 'Address is required',
      });
    }

    // Validate address format (permissive: requires number + space + text)
    const addressPattern = /^\d+[\s,].+$/i;
    if (!addressPattern.test(body.address.trim())) {
      return reply.code(400).send({
        error: 'Invalid address',
        message: 'Please enter a valid street address (e.g., 123 Main Street)',
      });
    }

    const limitReached = await checkPropertyLimit(reply, subscriptionId);
    if (!limitReached) return;

    try {
      // Check for duplicate
      const existing = await prisma.watchlistItem.findFirst({
        where: { subscriptionId, type: 'address', value: body.address.trim() },
      });

      if (existing) {
        return reply.code(409).send({
          error: 'Duplicate property',
          message: 'This property is already being monitored',
        });
      }

      const property = await prisma.watchlistItem.create({
        data: {
          subscriptionId,
          type: 'address',
          value: body.address.trim(),
        },
      });

      // Trigger initial scan
      try {
        const { homeTitleScheduler } = await import('@shieldai/hometitle');
        if (homeTitleScheduler.isRunning()) {
          await homeTitleScheduler.runScan();
        }
      } catch (scanError) {
        console.error('[HomeTitle] Initial scan error:', scanError);
      }

      return reply.code(201).send({
        property: {
          id: property.id,
          address: property.value,
          status: 'monitored',
          addedAt: property.createdAt,
        },
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to add property';
      return reply.code(422).send({ error: message });
    }
  });

  // DELETE /hometitle/properties/:id - Remove property from monitoring
  fastify.delete('/properties/:id', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    const id = (request.params as { id: string }).id;

    try {
      const result = await prisma.watchlistItem.update({
        where: { id, subscriptionId },
        data: { isActive: false },
      });

      return reply.send({
        property: {
          id: result.id,
          address: result.value,
          status: 'removed',
        },
      });
    } catch {
      return reply.code(404).send({
        error: 'Property not found',
        message: 'The monitored property does not exist or is not owned by this user',
      });
    }
  });

  // GET /hometitle/changes - Recent property changes
  fastify.get('/changes', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    const query = request.query as { limit?: number };
    const limit = Math.min(query.limit ?? 20, 50);

    try {
      const watchlistItemIds = (
        await prisma.watchlistItem.findMany({
          where: { subscriptionId, type: 'address', isActive: true },
          select: { id: true },
        })
      ).map((item) => item.id);

      const changes = await prisma.alert.findMany({
        where: {
          subscriptionId,
          watchlistItemId: { in: watchlistItemIds },
        },
        orderBy: { createdAt: 'desc' },
        take: limit,
        include: {
          watchlistItem: true,
        },
      });

      const formattedChanges = changes.map((alert) => ({
        id: alert.id,
        propertyAddress: alert.watchlistItem?.value ?? 'Unknown',
        type: 'property_change',
        severity: alert.severity,
        title: alert.title,
        message: alert.message,
        isRead: alert.isRead,
        createdAt: alert.createdAt,
      }));

      return reply.send({ changes: formattedChanges });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to fetch changes';
      return reply.code(500).send({ error: message });
    }
  });

  // GET /hometitle/alerts - Recent property alerts
  fastify.get('/alerts', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    const query = request.query as { limit?: number; severity?: string };
    const limit = Math.min(query.limit ?? 20, 50);

    try {
      const watchlistItemIds = (
        await prisma.watchlistItem.findMany({
          where: { subscriptionId, type: 'address', isActive: true },
          select: { id: true },
        })
      ).map((item) => item.id);

      const whereClause: Record<string, unknown> = {
        subscriptionId,
        watchlistItemId: { in: watchlistItemIds },
      };

      if (query.severity) {
        whereClause.severity = query.severity;
      }

      const alerts = await prisma.alert.findMany({
        where: whereClause,
        orderBy: { createdAt: 'desc' },
        take: limit,
        include: {
          watchlistItem: true,
        },
      });

      const formattedAlerts = alerts.map((alert) => ({
        id: alert.id,
        propertyAddress: alert.watchlistItem?.value ?? 'Unknown',
        severity: alert.severity,
        title: alert.title,
        message: alert.message,
        isRead: alert.isRead,
        channel: alert.channel,
        createdAt: alert.createdAt,
      }));

      return reply.send({ alerts: formattedAlerts });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Failed to fetch alerts';
      return reply.code(500).send({ error: message });
    }
  });

  // PATCH /hometitle/alerts/:id/read - Mark alert as read
  fastify.patch('/alerts/:id/read', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    const id = (request.params as { id: string }).id;

    try {
      const alert = await prisma.alert.update({
        where: { id, subscriptionId },
        data: { isRead: true, readAt: new Date() },
      });

      return reply.send({ alert: { id: alert.id, isRead: alert.isRead } });
    } catch {
      return reply.code(404).send({ error: 'Alert not found' });
    }
  });

  // POST /hometitle/scan - Trigger on-demand scan for all monitored properties
  fastify.post('/scan', async (request: FastifyRequest, reply: FastifyReply) => {
    const subscriptionId = await requirePremiumTier(request, reply);
    if (!subscriptionId) return;

    try {
      const { homeTitleScheduler } = await import('@shieldai/hometitle');
      const result = await homeTitleScheduler.runScan();

      return reply.send({
        scan: {
          scanId: result.scanId,
          propertiesScanned: result.propertiesScanned,
          changesDetected: result.changesDetected,
          alertsCreated: result.alertsCreated,
          notificationsSent: result.notificationsSent,
          startedAt: result.startedAt,
          completedAt: result.completedAt,
        },
      });
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Scan failed';
      return reply.code(500).send({ error: message });
    }
  });
}
