import { FastifyInstance } from 'fastify';
import bcrypt from 'bcryptjs';
import { prisma } from '../../../config/database';
import { authenticate } from '../middleware/auth';

interface UpdateSettingsBody {
  showOnlineStatus?: boolean;
  showCurrentlyWatching?: boolean;
  showInFriendActivity?: boolean;
  anonymizePublicComments?: boolean;
  hidePublicReactions?: boolean;
  hidePublicFinishes?: boolean;
  allowFriendRequests?: boolean;
  closeFriendsOnlyWatching?: boolean;
}

interface UpdateProfileBody {
  name?: string;
}

interface ChangePasswordBody {
  currentPassword: string;
  newPassword: string;
}

interface WatchHistoryQuery {
  limit?: string;
  offset?: string;
}

const PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{12,}$/;

export async function userProfileRoutes(fastify: FastifyInstance) {
  // ─── Stats ─────────────────────────────────────────────────

  /**
   * GET /me/stats
   * Returns UserStats (upsert if missing), recent daily activity, achievement count
   */
  fastify.get(
    '/me/stats',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;

      // Upsert UserStats (create with defaults if missing)
      const stats = await prisma.userStats.upsert({
        where: { userId },
        create: { userId },
        update: {},
      });

      // Last 30 days of daily activity
      const thirtyDaysAgo = new Date();
      thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
      const dateStr = thirtyDaysAgo.toISOString().split('T')[0];

      const dailyActivity = await prisma.userDailyActivity.findMany({
        where: {
          userId,
          activityDate: { gte: dateStr },
        },
        orderBy: { activityDate: 'asc' },
      });

      // Achievement count
      const achievementCount = await prisma.userAchievement.count({
        where: { userId },
      });

      return reply.send({
        stats,
        dailyActivity,
        achievementCount,
      });
    }
  );

  /**
   * GET /me/watch-history
   * Paginated recent VideoView records with video info
   */
  fastify.get<{ Querystring: WatchHistoryQuery }>(
    '/me/watch-history',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;
      const limit = Math.min(parseInt(request.query.limit || '20', 10), 50);
      const offset = parseInt(request.query.offset || '0', 10);

      const [views, total] = await Promise.all([
        prisma.videoView.findMany({
          where: { userId },
          orderBy: { createdAt: 'desc' },
          take: limit,
          skip: offset,
          select: {
            id: true,
            watchTimeSeconds: true,
            completed: true,
            createdAt: true,
            video: {
              select: {
                id: true,
                title: true,
                filename: true,
                thumbnailPath: true,
                durationSeconds: true,
              },
            },
          },
        }),
        prisma.videoView.count({ where: { userId } }),
      ]);

      return reply.send({ views, total, limit, offset });
    }
  );

  /**
   * POST /me/stats/recalculate
   * Recompute UserStats from raw data
   */
  fastify.post(
    '/me/stats/recalculate',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;

      // Aggregate from raw tables
      const [viewAgg, commentCount, reactionCount, finishCount, dailyActivity] =
        await Promise.all([
          prisma.videoView.aggregate({
            where: { userId },
            _sum: { watchTimeSeconds: true },
            _count: true,
          }),
          prisma.comment.count({ where: { userId } }),
          prisma.videoReaction.count({ where: { userId } }),
          prisma.userFinish.count({ where: { userId } }),
          prisma.userDailyActivity.findMany({
            where: { userId },
            orderBy: { activityDate: 'desc' },
            select: { activityDate: true, firstActivityHour: true },
          }),
        ]);

      // Calculate streaks from daily activity
      let currentStreak = 0;
      let longestStreak = 0;
      let tempStreak = 0;
      let nightOwlCount = 0;
      let earlyBirdCount = 0;

      // Sort descending (most recent first) for streak calculation
      const sortedDates = dailyActivity
        .map((d) => d.activityDate)
        .sort()
        .reverse();

      const today = new Date().toISOString().split('T')[0];

      for (let i = 0; i < sortedDates.length; i++) {
        const date = new Date(sortedDates[i]);
        const expected = new Date(today);
        expected.setDate(expected.getDate() - i);

        if (date.toISOString().split('T')[0] === expected.toISOString().split('T')[0]) {
          tempStreak++;
        } else {
          break;
        }
      }
      currentStreak = tempStreak;

      // Calculate longest streak (ascending order)
      const ascending = [...sortedDates].reverse();
      tempStreak = 1;
      for (let i = 1; i < ascending.length; i++) {
        const prev = new Date(ascending[i - 1]);
        const curr = new Date(ascending[i]);
        const diffMs = curr.getTime() - prev.getTime();
        const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24));

        if (diffDays === 1) {
          tempStreak++;
        } else {
          longestStreak = Math.max(longestStreak, tempStreak);
          tempStreak = 1;
        }
      }
      longestStreak = Math.max(longestStreak, tempStreak);
      if (ascending.length === 0) longestStreak = 0;

      // Night owl (activity hour >= 22 or < 5) vs Early bird (5-9)
      for (const d of dailyActivity) {
        if (d.firstActivityHour != null) {
          if (d.firstActivityHour >= 22 || d.firstActivityHour < 5) {
            nightOwlCount++;
          } else if (d.firstActivityHour >= 5 && d.firstActivityHour < 10) {
            earlyBirdCount++;
          }
        }
      }

      // Longest single session from VideoView
      const longestSession = await prisma.videoView.aggregate({
        where: { userId },
        _max: { watchTimeSeconds: true },
      });

      const stats = await prisma.userStats.upsert({
        where: { userId },
        create: {
          userId,
          totalWatchTimeSeconds: viewAgg._sum.watchTimeSeconds || 0,
          totalVideosWatched: viewAgg._count,
          totalCommentsMade: commentCount,
          totalUpvotesGiven: reactionCount,
          totalFinishes: finishCount,
          currentDayStreak: currentStreak,
          longestDayStreak: longestStreak,
          nightOwlCount,
          earlyBirdCount,
          longestSingleSession: longestSession._max.watchTimeSeconds || 0,
          lastActiveDate: today,
          updatedAt: new Date(),
        },
        update: {
          totalWatchTimeSeconds: viewAgg._sum.watchTimeSeconds || 0,
          totalVideosWatched: viewAgg._count,
          totalCommentsMade: commentCount,
          totalUpvotesGiven: reactionCount,
          totalFinishes: finishCount,
          currentDayStreak: currentStreak,
          longestDayStreak: longestStreak,
          nightOwlCount,
          earlyBirdCount,
          longestSingleSession: longestSession._max.watchTimeSeconds || 0,
          lastActiveDate: today,
          updatedAt: new Date(),
        },
      });

      return reply.send({ stats, recalculated: true });
    }
  );

  // ─── Settings ──────────────────────────────────────────────

  /**
   * GET /me/settings
   * Returns PrivacySettings (upsert defaults if missing) + user profile
   */
  fastify.get(
    '/me/settings',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;

      const [privacy, user] = await Promise.all([
        prisma.privacySettings.upsert({
          where: { userId },
          create: { userId },
          update: {},
        }),
        prisma.user.findUnique({
          where: { id: userId },
          select: { name: true, email: true },
        }),
      ]);

      return reply.send({ privacy, profile: user });
    }
  );

  /**
   * PUT /me/settings
   * Update PrivacySettings boolean toggles
   */
  fastify.put<{ Body: UpdateSettingsBody }>(
    '/me/settings',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;
      const body = request.body;

      // Only allow known boolean fields
      const allowedFields: (keyof UpdateSettingsBody)[] = [
        'showOnlineStatus',
        'showCurrentlyWatching',
        'showInFriendActivity',
        'anonymizePublicComments',
        'hidePublicReactions',
        'hidePublicFinishes',
        'allowFriendRequests',
        'closeFriendsOnlyWatching',
      ];

      const updateData: Record<string, boolean> = {};
      for (const field of allowedFields) {
        if (typeof body[field] === 'boolean') {
          updateData[field] = body[field] as boolean;
        }
      }

      const privacy = await prisma.privacySettings.upsert({
        where: { userId },
        create: { userId, ...updateData, updatedAt: new Date() },
        update: { ...updateData, updatedAt: new Date() },
      });

      return reply.send({ privacy });
    }
  );

  /**
   * PUT /me/profile
   * Update user name (email is read-only)
   */
  fastify.put<{ Body: UpdateProfileBody }>(
    '/me/profile',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;
      const { name } = request.body;

      if (name !== undefined && (typeof name !== 'string' || name.length > 100)) {
        return reply.code(400).send({ message: 'Name must be a string under 100 characters' });
      }

      const user = await prisma.user.update({
        where: { id: userId },
        data: { name: name?.trim() || null },
        select: { name: true, email: true },
      });

      return reply.send({ profile: user });
    }
  );

  /**
   * PUT /me/password
   * Change password (requires current password verification)
   */
  fastify.put<{ Body: ChangePasswordBody }>(
    '/me/password',
    { preHandler: [authenticate] },
    async (request, reply) => {
      const userId = request.user!.id;
      const { currentPassword, newPassword } = request.body;

      if (!currentPassword || !newPassword) {
        return reply
          .code(400)
          .send({ message: 'Current password and new password are required' });
      }

      // Validate new password meets policy
      if (!PASSWORD_REGEX.test(newPassword)) {
        return reply.code(400).send({
          message:
            'Password must be at least 12 characters with uppercase, lowercase, and a digit',
        });
      }

      // Fetch current password hash
      const user = await prisma.user.findUnique({
        where: { id: userId },
        select: { password: true },
      });

      if (!user) {
        return reply.code(404).send({ message: 'User not found' });
      }

      // Verify current password
      const isValid = await bcrypt.compare(currentPassword, user.password);
      if (!isValid) {
        return reply.code(401).send({ message: 'Current password is incorrect' });
      }

      // Hash and save new password, invalidate all sessions
      const hashedPassword = await bcrypt.hash(newPassword, 12);
      await prisma.$transaction([
        prisma.user.update({
          where: { id: userId },
          data: { password: hashedPassword },
        }),
        prisma.refreshToken.deleteMany({ where: { userId } }),
      ]);

      return reply.send({ message: 'Password updated successfully' });
    }
  );
}
