const express = require("express");
const auth = require("../middleware/auth");
const { asyncHandler } = require("../middleware/errorHandler");
const { getCacheMiddleware, invalidateCacheByPattern } = require("../middleware/cache");
const couchdbService = require("../services/couchdbService");
const router = express.Router();

/**
 * Parse timeframe parameter to date filter
 */
const getTimeframeFilter = (timeframe = "all") => {
  const now = new Date();
  let startDate = null;

  switch (timeframe) {
    case "7d":
      startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
      break;
    case "30d":
      startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
      break;
    case "90d":
      startDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
      break;
    case "all":
    default:
      return null;
  }

  return startDate ? startDate.toISOString() : null;
};

/**
 * Group data by time period (day, week, month)
 */
const groupByTimePeriod = (data, groupBy = "day", dateField = "createdAt") => {
  const grouped = {};

  data.forEach((item) => {
    const date = new Date(item[dateField]);
    let key;

    switch (groupBy) {
      case "week":
        const weekStart = new Date(date);
        weekStart.setDate(date.getDate() - date.getDay());
        key = weekStart.toISOString().split("T")[0];
        break;
      case "month":
        key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
        break;
      case "day":
      default:
        key = date.toISOString().split("T")[0];
        break;
    }

    if (!grouped[key]) {
      grouped[key] = [];
    }
    grouped[key].push(item);
  });

  return Object.keys(grouped)
    .sort()
    .map((key) => ({
      period: key,
      count: grouped[key].length,
      items: grouped[key],
    }));
};

/**
 * GET /api/analytics/overview
 * Get overall platform statistics
 */
router.get(
  "/overview",
  auth,
  getCacheMiddleware(300), // Cache for 5 minutes
  asyncHandler(async (req, res) => {
    const { timeframe = "all" } = req.query;
    const startDate = getTimeframeFilter(timeframe);

    // Build queries
    const userQuery = { selector: { type: "user" } };
    const streetQuery = { selector: { type: "street" } };
    const taskQuery = { selector: { type: "task" } };
    const eventQuery = { selector: { type: "event" } };
    const postQuery = { selector: { type: "post" } };

    // Add timeframe filters if specified
    if (startDate) {
      taskQuery.selector.createdAt = { $gte: startDate };
      eventQuery.selector.createdAt = { $gte: startDate };
      postQuery.selector.createdAt = { $gte: startDate };
    }

    // Execute queries in parallel
    const [users, streets, tasks, events, posts] = await Promise.all([
      couchdbService.find(userQuery),
      couchdbService.find(streetQuery),
      couchdbService.find(taskQuery),
      couchdbService.find(eventQuery),
      couchdbService.find(postQuery),
    ]);

    // Calculate statistics
    const adoptedStreets = streets.filter((s) => s.status === "adopted").length;
    const completedTasks = tasks.filter((t) => t.status === "completed").length;
    const activeEvents = events.filter((e) => e.status === "upcoming").length;
    const totalPoints = users.reduce((sum, user) => sum + (user.points || 0), 0);
    const averagePointsPerUser = users.length > 0 ? Math.round(totalPoints / users.length) : 0;

    res.json({
      overview: {
        totalUsers: users.length,
        totalStreets: streets.length,
        adoptedStreets,
        availableStreets: streets.length - adoptedStreets,
        totalTasks: tasks.length,
        completedTasks,
        pendingTasks: tasks.length - completedTasks,
        totalEvents: events.length,
        activeEvents,
        completedEvents: events.filter((e) => e.status === "completed").length,
        totalPosts: posts.length,
        totalPoints,
        averagePointsPerUser,
      },
      timeframe,
    });
  }),
);

/**
 * GET /api/analytics/user/:userId
 * Get user-specific analytics
 */
router.get(
  "/user/:userId",
  auth,
  getCacheMiddleware(300), // Cache for 5 minutes
  asyncHandler(async (req, res) => {
    const { userId } = req.params;
    const { timeframe = "all" } = req.query;
    const startDate = getTimeframeFilter(timeframe);

    // Get user
    const user = await couchdbService.findUserById(userId);
    if (!user) {
      return res.status(404).json({ msg: "User not found" });
    }

    // Build queries for user's activity
    const taskQuery = {
      selector: {
        type: "task",
        "completedBy.userId": userId,
      },
    };
    const postQuery = {
      selector: {
        type: "post",
        "user.userId": userId,
      },
    };
    const eventQuery = {
      selector: {
        type: "event",
        participants: {
          $elemMatch: { userId: userId },
        },
      },
    };
    const transactionQuery = {
      selector: {
        type: "point_transaction",
        "user.userId": userId,
      },
    };

    // Add timeframe filters if specified
    if (startDate) {
      taskQuery.selector.createdAt = { $gte: startDate };
      postQuery.selector.createdAt = { $gte: startDate };
      eventQuery.selector.createdAt = { $gte: startDate };
      transactionQuery.selector.createdAt = { $gte: startDate };
    }

    // Execute queries in parallel
    const [tasks, posts, events, transactions] = await Promise.all([
      couchdbService.find(taskQuery),
      couchdbService.find(postQuery),
      couchdbService.find(eventQuery),
      couchdbService.find(transactionQuery),
    ]);

    // Get adopted streets
    const adoptedStreetsDetails = await Promise.all(
      (user.adoptedStreets || []).map((streetId) => couchdbService.getDocument(streetId)),
    );

    // Calculate points earned/spent
    const pointsEarned = transactions
      .filter((t) => t.amount > 0)
      .reduce((sum, t) => sum + t.amount, 0);
    const pointsSpent = transactions
      .filter((t) => t.amount < 0)
      .reduce((sum, t) => sum + Math.abs(t.amount), 0);

    // Calculate engagement metrics
    const totalLikesReceived = posts.reduce((sum, post) => sum + (post.likesCount || 0), 0);
    const totalCommentsReceived = posts.reduce((sum, post) => sum + (post.commentsCount || 0), 0);

    res.json({
      user: {
        id: user._id,
        name: user.name,
        email: user.email,
        points: user.points || 0,
        isPremium: user.isPremium || false,
      },
      stats: {
        streetsAdopted: adoptedStreetsDetails.filter(Boolean).length,
        tasksCompleted: tasks.length,
        postsCreated: posts.length,
        eventsParticipated: events.length,
        badgesEarned: (user.earnedBadges || []).length,
        pointsEarned,
        pointsSpent,
        totalLikesReceived,
        totalCommentsReceived,
      },
      recentActivity: {
        tasks: tasks.slice(0, 5),
        posts: posts.slice(0, 5),
        events: events.slice(0, 5),
      },
      timeframe,
    });
  }),
);

/**
 * GET /api/analytics/activity
 * Get activity over time
 */
router.get(
  "/activity",
  auth,
  getCacheMiddleware(300), // Cache for 5 minutes
  asyncHandler(async (req, res) => {
    const { timeframe = "30d", groupBy = "day" } = req.query;
    const startDate = getTimeframeFilter(timeframe);

    // Build queries
    const taskQuery = { selector: { type: "task" } };
    const postQuery = { selector: { type: "post" } };
    const eventQuery = { selector: { type: "event" } };
    const streetQuery = { selector: { type: "street", status: "adopted" } };

    // Add timeframe filters
    if (startDate) {
      taskQuery.selector.createdAt = { $gte: startDate };
      postQuery.selector.createdAt = { $gte: startDate };
      eventQuery.selector.createdAt = { $gte: startDate };
      streetQuery.selector["adoptedBy.userId"] = { $exists: true };
    }

    // Execute queries in parallel
    const [tasks, posts, events, streets] = await Promise.all([
      couchdbService.find(taskQuery),
      couchdbService.find(postQuery),
      couchdbService.find(eventQuery),
      couchdbService.find(streetQuery),
    ]);

    // Filter by timeframe
    const filterByTimeframe = (items) => {
      if (!startDate) return items;
      return items.filter((item) => {
        const itemDate = new Date(item.createdAt);
        return itemDate >= new Date(startDate);
      });
    };

    const filteredTasks = filterByTimeframe(tasks);
    const filteredPosts = filterByTimeframe(posts);
    const filteredEvents = filterByTimeframe(events);
    const filteredStreets = filterByTimeframe(streets);

    // Group by time period
    const groupedTasks = groupByTimePeriod(filteredTasks, groupBy);
    const groupedPosts = groupByTimePeriod(filteredPosts, groupBy);
    const groupedEvents = groupByTimePeriod(filteredEvents, groupBy);
    const groupedStreets = groupByTimePeriod(filteredStreets, groupBy);

    // Combine all periods
    const allPeriods = new Set([
      ...groupedTasks.map((g) => g.period),
      ...groupedPosts.map((g) => g.period),
      ...groupedEvents.map((g) => g.period),
      ...groupedStreets.map((g) => g.period),
    ]);

    const activityData = Array.from(allPeriods)
      .sort()
      .map((period) => ({
        period,
        tasks: groupedTasks.find((g) => g.period === period)?.count || 0,
        posts: groupedPosts.find((g) => g.period === period)?.count || 0,
        events: groupedEvents.find((g) => g.period === period)?.count || 0,
        streetsAdopted: groupedStreets.find((g) => g.period === period)?.count || 0,
      }));

    res.json({
      activity: activityData,
      timeframe,
      groupBy,
      summary: {
        totalTasks: filteredTasks.length,
        totalPosts: filteredPosts.length,
        totalEvents: filteredEvents.length,
        totalStreetsAdopted: filteredStreets.length,
      },
    });
  }),
);

/**
 * GET /api/analytics/top-contributors
 * Get top contributing users
 */
router.get(
  "/top-contributors",
  auth,
  getCacheMiddleware(300), // Cache for 5 minutes
  asyncHandler(async (req, res) => {
    const { limit = 10, timeframe = "all", metric = "points" } = req.query;
    const startDate = getTimeframeFilter(timeframe);

    // Get all users
    const users = await couchdbService.find({
      selector: { type: "user" },
    });

    // If timeframe is specified, calculate contributions within that timeframe
    let contributors;

    if (startDate && metric !== "points") {
      // For time-based metrics, query activities
      const contributorsWithActivity = await Promise.all(
        users.map(async (user) => {
          const taskQuery = {
            selector: {
              type: "task",
              "completedBy.userId": user._id,
              createdAt: { $gte: startDate },
            },
          };
          const postQuery = {
            selector: {
              type: "post",
              "user.userId": user._id,
              createdAt: { $gte: startDate },
            },
          };
          const streetQuery = {
            selector: {
              type: "street",
              "adoptedBy.userId": user._id,
            },
          };

          const [tasks, posts, streets] = await Promise.all([
            couchdbService.find(taskQuery),
            couchdbService.find(postQuery),
            couchdbService.find(streetQuery),
          ]);

          let score = 0;
          switch (metric) {
            case "tasks":
              score = tasks.length;
              break;
            case "posts":
              score = posts.length;
              break;
            case "streets":
              score = streets.length;
              break;
            default:
              score = user.points || 0;
          }

          return {
            userId: user._id,
            name: user.name,
            email: user.email,
            profilePicture: user.profilePicture,
            isPremium: user.isPremium,
            score,
            stats: {
              points: user.points || 0,
              tasksCompleted: tasks.length,
              postsCreated: posts.length,
              streetsAdopted: streets.length,
              badgesEarned: (user.earnedBadges || []).length,
            },
          };
        }),
      );

      contributors = contributorsWithActivity
        .filter((c) => c.score > 0)
        .sort((a, b) => b.score - a.score)
        .slice(0, parseInt(limit));
    } else {
      // For all-time or points metric, use user data directly
      contributors = users
        .map((user) => {
          let score = 0;
          switch (metric) {
            case "tasks":
              score = user.stats?.tasksCompleted || 0;
              break;
            case "posts":
              score = user.stats?.postsCreated || 0;
              break;
            case "streets":
              score = user.stats?.streetsAdopted || 0;
              break;
            default:
              score = user.points || 0;
          }

          return {
            userId: user._id,
            name: user.name,
            email: user.email,
            profilePicture: user.profilePicture,
            isPremium: user.isPremium,
            score,
            stats: {
              points: user.points || 0,
              tasksCompleted: user.stats?.tasksCompleted || 0,
              postsCreated: user.stats?.postsCreated || 0,
              streetsAdopted: user.stats?.streetsAdopted || 0,
              badgesEarned: (user.earnedBadges || []).length,
            },
          };
        })
        .filter((c) => c.score > 0)
        .sort((a, b) => b.score - a.score)
        .slice(0, parseInt(limit));
    }

    res.json({
      contributors,
      metric,
      timeframe,
      limit: parseInt(limit),
    });
  }),
);

/**
 * GET /api/analytics/street-stats
 * Get street adoption and task completion statistics
 */
router.get(
  "/street-stats",
  auth,
  getCacheMiddleware(300), // Cache for 5 minutes
  asyncHandler(async (req, res) => {
    const { timeframe = "all" } = req.query;
    const startDate = getTimeframeFilter(timeframe);

    // Get all streets
    const streets = await couchdbService.find({
      selector: { type: "street" },
    });

    // Get all tasks
    const taskQuery = { selector: { type: "task" } };
    if (startDate) {
      taskQuery.selector.createdAt = { $gte: startDate };
    }
    const tasks = await couchdbService.find(taskQuery);

    // Calculate street statistics
    const totalStreets = streets.length;
    const adoptedStreets = streets.filter((s) => s.status === "adopted").length;
    const availableStreets = streets.filter((s) => s.status === "available").length;

    const adoptionRate = totalStreets > 0 ? ((adoptedStreets / totalStreets) * 100).toFixed(2) : 0;

    // Task statistics
    const totalTasks = tasks.length;
    const completedTasks = tasks.filter((t) => t.status === "completed").length;
    const pendingTasks = tasks.filter((t) => t.status === "pending").length;
    const inProgressTasks = tasks.filter((t) => t.status === "in_progress").length;

    const completionRate = totalTasks > 0 ? ((completedTasks / totalTasks) * 100).toFixed(2) : 0;

    // Top streets by task completion
    const streetTaskCounts = {};
    tasks
      .filter((t) => t.status === "completed" && t.street?.streetId)
      .forEach((task) => {
        const streetId = task.street.streetId;
        if (!streetTaskCounts[streetId]) {
          streetTaskCounts[streetId] = {
            streetId,
            streetName: task.street.name,
            count: 0,
          };
        }
        streetTaskCounts[streetId].count++;
      });

    const topStreets = Object.values(streetTaskCounts)
      .sort((a, b) => b.count - a.count)
      .slice(0, 10);

    res.json({
      adoption: {
        totalStreets,
        adoptedStreets,
        availableStreets,
        adoptionRate: parseFloat(adoptionRate),
      },
      tasks: {
        totalTasks,
        completedTasks,
        pendingTasks,
        inProgressTasks,
        completionRate: parseFloat(completionRate),
      },
      topStreets,
      timeframe,
    });
  }),
);

module.exports = router;
