import { prisma } from '../../config/database';
import { redis } from '../../config/redis';
import { friendshipService } from './friendship.service';

/** A unified feed item representing any activity type */
export interface FeedItem {
  id: string;
  type: 'shift_signup' | 'campaign_email' | 'canvass_session' | 'response_submitted' | 'impact_story' | 'volunteer_featured' | 'referral_completed' | 'challenge_completed' | 'poll_voted';
  userId: string;
  userName: string | null;
  userEmail: string;
  title: string;
  description: string;
  metadata: Record<string, unknown>;
  timestamp: Date;
}

const FEED_CACHE_TTL = 120; // 2 minutes
const FEED_MAX_AGE_DAYS = 30;
const FEED_MAX_ITEMS = 50;

export const feedService = {
  /**
   * Get a combined feed of friends' recent activities.
   * Aggregates shift signups, campaign emails, canvass sessions, and response submissions.
   * Results are cached in Redis for 2 minutes.
   */
  async getFriendFeed(userId: string, page: number, limit: number) {
    const cacheKey = `social:feed:${userId}:${page}:${limit}`;

    // Check cache
    const cached = await redis.get(cacheKey);
    if (cached) {
      return JSON.parse(cached);
    }

    // Get friend IDs + their privacy settings
    const friendIds = await friendshipService.getFriendIds(userId);
    if (friendIds.length === 0) {
      return { items: [], pagination: { page, limit, total: 0, totalPages: 0 } };
    }

    // Filter out friends who have showInFriendActivity disabled
    const privacySettings = await prisma.privacySettings.findMany({
      where: { userId: { in: friendIds }, showInFriendActivity: false },
      select: { userId: true },
    });
    const hiddenIds = new Set(privacySettings.map((p) => p.userId));
    const visibleFriendIds = friendIds.filter((id) => !hiddenIds.has(id));

    if (visibleFriendIds.length === 0) {
      return { items: [], pagination: { page, limit, total: 0, totalPages: 0 } };
    }

    const since = new Date();
    since.setDate(since.getDate() - FEED_MAX_AGE_DAYS);

    // Query all activity types in parallel
    const [shiftSignups, campaignEmails, canvassSessions, responses, impactStories, spotlights, referrals, challenges, pollVotes] = await Promise.all([
      this.getShiftSignupActivities(visibleFriendIds, since),
      this.getCampaignEmailActivities(visibleFriendIds, since),
      this.getCanvassSessionActivities(visibleFriendIds, since),
      this.getResponseActivities(visibleFriendIds, since),
      this.getImpactStoryActivities(since),
      this.getSpotlightActivities(since),
      this.getReferralActivities(visibleFriendIds, since),
      this.getChallengeActivities(since),
      this.getStrawPollVoteActivities(visibleFriendIds, since),
    ]);

    // Merge and sort by timestamp descending
    const allItems: FeedItem[] = [
      ...shiftSignups,
      ...campaignEmails,
      ...canvassSessions,
      ...responses,
      ...impactStories,
      ...spotlights,
      ...referrals,
      ...challenges,
      ...pollVotes,
    ].sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());

    // Cap total items
    const cappedItems = allItems.slice(0, FEED_MAX_ITEMS);
    const total = cappedItems.length;
    const totalPages = Math.ceil(total / limit);
    const skip = (page - 1) * limit;
    const items = cappedItems.slice(skip, skip + limit);

    const result = {
      items,
      pagination: { page, limit, total, totalPages },
    };

    // Cache for 2 minutes
    await redis.setex(cacheKey, FEED_CACHE_TTL, JSON.stringify(result));

    return result;
  },

  /** Get own activity for profile display */
  async getMyActivity(userId: string, page: number, limit: number) {
    const since = new Date();
    since.setDate(since.getDate() - FEED_MAX_AGE_DAYS);

    const [shiftSignups, campaignEmails, canvassSessions, responses, referrals] = await Promise.all([
      this.getShiftSignupActivities([userId], since),
      this.getCampaignEmailActivities([userId], since),
      this.getCanvassSessionActivities([userId], since),
      this.getResponseActivities([userId], since),
      this.getReferralActivities([userId], since),
    ]);

    const allItems: FeedItem[] = [
      ...shiftSignups,
      ...campaignEmails,
      ...canvassSessions,
      ...responses,
      ...referrals,
    ].sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());

    const total = allItems.length;
    const totalPages = Math.ceil(total / limit);
    const skip = (page - 1) * limit;
    const items = allItems.slice(skip, skip + limit);

    return { items, pagination: { page, limit, total, totalPages } };
  },

  // --- Activity type queries ---

  async getShiftSignupActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const signups = await prisma.shiftSignup.findMany({
      where: {
        userId: { in: userIds },
        signupDate: { gte: since },
        status: 'CONFIRMED',
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
        shift: { select: { id: true, title: true, startTime: true } },
      },
      orderBy: { signupDate: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return signups
      .filter((s) => s.user)
      .map((s) => ({
        id: `shift_signup:${s.id}`,
        type: 'shift_signup' as const,
        userId: s.user!.id,
        userName: s.user!.name,
        userEmail: s.user!.email,
        title: 'Signed up for a shift',
        description: s.shift.title,
        metadata: { shiftId: s.shift.id, shiftTitle: s.shift.title, startTime: s.shift.startTime },
        timestamp: s.signupDate,
      }));
  },

  async getCampaignEmailActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const emails = await prisma.campaignEmail.findMany({
      where: {
        userId: { in: userIds },
        sentAt: { gte: since },
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
        campaign: { select: { id: true, title: true, slug: true } },
      },
      orderBy: { sentAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return emails
      .filter((e) => e.user)
      .map((e) => ({
        id: `campaign_email:${e.id}`,
        type: 'campaign_email' as const,
        userId: e.user!.id,
        userName: e.user!.name,
        userEmail: e.user!.email,
        title: 'Participated in a campaign',
        description: e.campaign.title,
        metadata: { campaignId: e.campaign.id, campaignSlug: e.campaign.slug },
        timestamp: e.sentAt!,
      }));
  },

  async getCanvassSessionActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const sessions = await prisma.canvassSession.findMany({
      where: {
        userId: { in: userIds },
        startedAt: { gte: since },
        status: 'COMPLETED',
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
        cut: { select: { id: true, name: true } },
        _count: { select: { visits: true } },
      },
      orderBy: { startedAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return sessions.map((s) => ({
      id: `canvass_session:${s.id}`,
      type: 'canvass_session' as const,
      userId: s.user.id,
      userName: s.user.name,
      userEmail: s.user.email,
      title: 'Completed a canvass session',
      description: `${s._count.visits} doors in ${s.cut.name}`,
      metadata: { cutId: s.cut.id, cutName: s.cut.name, visitCount: s._count.visits },
      timestamp: s.startedAt,
    }));
  },

  async getResponseActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const responses = await prisma.representativeResponse.findMany({
      where: {
        submittedByUserId: { in: userIds },
        createdAt: { gte: since },
        status: 'APPROVED',
      },
      include: {
        submittedByUser: { select: { id: true, name: true, email: true } },
        campaign: { select: { id: true, title: true, slug: true } },
      },
      orderBy: { createdAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return responses
      .filter((r) => r.submittedByUser)
      .map((r) => ({
        id: `response:${r.id}`,
        type: 'response_submitted' as const,
        userId: r.submittedByUser!.id,
        userName: r.submittedByUser!.name,
        userEmail: r.submittedByUser!.email,
        title: 'Submitted a representative response',
        description: `Response from ${r.representativeName} on ${r.campaign.title}`,
        metadata: { campaignId: r.campaign.id, representativeName: r.representativeName },
        timestamp: r.createdAt,
      }));
  },

  async getImpactStoryActivities(since: Date): Promise<FeedItem[]> {
    const stories = await prisma.impactStory.findMany({
      where: {
        status: 'PUBLISHED',
        publishedAt: { gte: since },
      },
      include: {
        campaign: { select: { id: true, title: true, slug: true } },
        createdBy: { select: { id: true, name: true, email: true } },
      },
      orderBy: { publishedAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return stories.map((s) => ({
      id: `impact_story:${s.id}`,
      type: 'impact_story' as const,
      userId: s.createdBy?.id || '',
      userName: s.createdBy?.name || null,
      userEmail: s.createdBy?.email || '',
      title: s.title,
      description: `${s.campaign.title}${s.milestoneValue ? ` — ${s.milestoneValue} ${s.milestoneMetric || 'milestone'}` : ''}`,
      metadata: { campaignId: s.campaign.id, campaignSlug: s.campaign.slug, storyType: s.type },
      timestamp: s.publishedAt || s.createdAt,
    }));
  },

  async getSpotlightActivities(since: Date): Promise<FeedItem[]> {
    const spotlights = await prisma.volunteerSpotlight.findMany({
      where: {
        status: 'FEATURED',
        updatedAt: { gte: since },
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
      },
      orderBy: { updatedAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return spotlights.map((s) => ({
      id: `spotlight:${s.id}`,
      type: 'volunteer_featured' as const,
      userId: s.user.id,
      userName: s.user.name,
      userEmail: s.user.email,
      title: 'Featured as Volunteer Spotlight',
      description: s.headline || `Spotlight for ${s.featuredMonth || 'this month'}`,
      metadata: { spotlightId: s.id, featuredMonth: s.featuredMonth },
      timestamp: s.updatedAt,
    }));
  },

  async getReferralActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const referrals = await prisma.referral.findMany({
      where: {
        referrerId: { in: userIds },
        completedAt: { gte: since },
      },
      include: {
        referrer: { select: { id: true, name: true, email: true } },
        referredUser: { select: { id: true, name: true } },
      },
      orderBy: { completedAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return referrals.map((r) => ({
      id: `referral:${r.id}`,
      type: 'referral_completed' as const,
      userId: r.referrer.id,
      userName: r.referrer.name,
      userEmail: r.referrer.email,
      title: 'Referred a new member',
      description: `${r.referredUser.name || 'A new member'} joined the platform`,
      metadata: { referredUserId: r.referredUser.id },
      timestamp: r.completedAt,
    }));
  },

  async getChallengeActivities(since: Date): Promise<FeedItem[]> {
    const challenges = await prisma.challenge.findMany({
      where: {
        status: 'COMPLETED',
        updatedAt: { gte: since },
      },
      include: {
        teams: {
          orderBy: { score: 'desc' },
          take: 1,
          include: {
            captain: { select: { id: true, name: true, email: true } },
          },
        },
      },
      orderBy: { updatedAt: 'desc' },
      take: FEED_MAX_ITEMS,
    });

    return challenges
      .filter((c) => c.teams.length > 0)
      .map((c) => {
        const winner = c.teams[0];
        return {
          id: `challenge:${c.id}`,
          type: 'challenge_completed' as const,
          userId: winner.captain.id,
          userName: winner.captain.name,
          userEmail: winner.captain.email,
          title: `Challenge completed: ${c.title}`,
          description: `Team "${winner.name}" won with ${winner.score} points`,
          metadata: { challengeId: c.id, winningTeamId: winner.id, metric: c.metric },
          timestamp: c.updatedAt,
        };
      });
  },

  async getStrawPollVoteActivities(userIds: string[], since: Date): Promise<FeedItem[]> {
    const votes = await prisma.strawPollVote.findMany({
      where: {
        userId: { in: userIds },
        createdAt: { gte: since },
      },
      include: {
        user: { select: { id: true, name: true, email: true } },
        poll: { select: { id: true, slug: true, title: true } },
        option: { select: { label: true } },
      },
      orderBy: { createdAt: 'desc' },
      take: 20,
    });

    return votes
      .filter((v) => v.user)
      .map((v) => ({
        id: `poll_vote:${v.id}`,
        type: 'poll_voted' as const,
        userId: v.user!.id,
        userName: v.user!.name,
        userEmail: v.user!.email,
        title: `Voted on "${v.poll.title}"`,
        description: `Chose: ${v.option.label}`,
        metadata: { pollId: v.poll.id, pollSlug: v.poll.slug },
        timestamp: v.createdAt,
      }));
  },
};
