import { prisma } from '../../config/database';
import { SignupStatus } from '@prisma/client';
import { notificationService } from './notification.service';
import { logger } from '../../utils/logger';

/** Achievement definition */
interface AchievementDef {
  id: string;
  name: string;
  description: string;
  icon: string;
  category: 'shifts' | 'canvass' | 'campaigns' | 'social';
  threshold: number;
  /** Function to compute current progress for a user */
  getProgress: (userId: string) => Promise<number>;
}

/** Static achievement registry */
const ACHIEVEMENTS: AchievementDef[] = [
  // Shift achievements
  {
    id: 'FIRST_SHIFT',
    name: 'First Steps',
    description: 'Sign up for your first volunteer shift',
    icon: 'calendar',
    category: 'shifts',
    threshold: 1,
    getProgress: async (userId) => {
      const user = await prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
      if (!user) return 0;
      return prisma.shiftSignup.count({
        where: { userEmail: user.email, status: SignupStatus.CONFIRMED },
      });
    },
  },
  {
    id: 'SHIFT_STREAK_3',
    name: 'Reliable Volunteer',
    description: 'Sign up for 3 volunteer shifts',
    icon: 'calendar',
    category: 'shifts',
    threshold: 3,
    getProgress: async (userId) => {
      const user = await prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
      if (!user) return 0;
      return prisma.shiftSignup.count({
        where: { userEmail: user.email, status: SignupStatus.CONFIRMED },
      });
    },
  },
  {
    id: 'SHIFT_STREAK_10',
    name: 'Shift Champion',
    description: 'Sign up for 10 volunteer shifts',
    icon: 'trophy',
    category: 'shifts',
    threshold: 10,
    getProgress: async (userId) => {
      const user = await prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
      if (!user) return 0;
      return prisma.shiftSignup.count({
        where: { userEmail: user.email, status: SignupStatus.CONFIRMED },
      });
    },
  },
  // Canvass achievements
  {
    id: 'FIRST_CANVASS',
    name: 'Door Knocker',
    description: 'Complete your first canvass session',
    icon: 'environment',
    category: 'canvass',
    threshold: 1,
    getProgress: async (userId) =>
      prisma.canvassSession.count({
        where: { userId, status: 'COMPLETED' },
      }),
  },
  {
    id: 'CANVASS_50_DOORS',
    name: 'Neighbourhood Explorer',
    description: 'Record 50 canvass visits',
    icon: 'home',
    category: 'canvass',
    threshold: 50,
    getProgress: async (userId) =>
      prisma.canvassVisit.count({
        where: { session: { userId } },
      }),
  },
  {
    id: 'CANVASS_100_DOORS',
    name: 'Community Connector',
    description: 'Record 100 canvass visits',
    icon: 'home',
    category: 'canvass',
    threshold: 100,
    getProgress: async (userId) =>
      prisma.canvassVisit.count({
        where: { session: { userId } },
      }),
  },
  {
    id: 'CANVASS_500_DOORS',
    name: 'Door-to-Door Legend',
    description: 'Record 500 canvass visits',
    icon: 'star',
    category: 'canvass',
    threshold: 500,
    getProgress: async (userId) =>
      prisma.canvassVisit.count({
        where: { session: { userId } },
      }),
  },
  // Campaign achievements
  {
    id: 'FIRST_CAMPAIGN_EMAIL',
    name: 'Voice Heard',
    description: 'Send your first advocacy email',
    icon: 'mail',
    category: 'campaigns',
    threshold: 1,
    getProgress: async (userId) =>
      prisma.campaignEmail.count({
        where: { userId },
      }),
  },
  {
    id: 'CAMPAIGN_CHAMPION',
    name: 'Campaign Champion',
    description: 'Participate in 5 different campaigns',
    icon: 'fire',
    category: 'campaigns',
    threshold: 5,
    getProgress: async (userId) => {
      const result = await prisma.campaignEmail.findMany({
        where: { userId },
        distinct: ['campaignId'],
        select: { campaignId: true },
      });
      return result.length;
    },
  },
  // Social achievements
  {
    id: 'SOCIAL_BUTTERFLY',
    name: 'Social Butterfly',
    description: 'Make 10 friends on the platform',
    icon: 'team',
    category: 'social',
    threshold: 10,
    getProgress: async (userId) =>
      prisma.friendship.count({
        where: {
          status: 'accepted',
          OR: [{ userId }, { friendId: userId }],
        },
      }),
  },
  {
    id: 'TEAM_PLAYER',
    name: 'Team Player',
    description: 'Be a member of 3 groups',
    icon: 'usergroup-add',
    category: 'social',
    threshold: 3,
    getProgress: async (userId) =>
      prisma.socialGroupMember.count({
        where: { userId },
      }),
  },
  // Referral achievements
  {
    id: 'FIRST_REFERRAL',
    name: 'Ambassador',
    description: 'Refer your first friend to the platform',
    icon: 'usergroup-add',
    category: 'social',
    threshold: 1,
    getProgress: async (userId) =>
      prisma.referral.count({ where: { referrerId: userId } }),
  },
  {
    id: 'REFERRAL_5',
    name: 'Recruiter',
    description: 'Refer 5 friends to the platform',
    icon: 'usergroup-add',
    category: 'social',
    threshold: 5,
    getProgress: async (userId) =>
      prisma.referral.count({ where: { referrerId: userId } }),
  },
  {
    id: 'REFERRAL_25',
    name: 'Movement Builder',
    description: 'Refer 25 friends to the platform',
    icon: 'star',
    category: 'social',
    threshold: 25,
    getProgress: async (userId) =>
      prisma.referral.count({ where: { referrerId: userId } }),
  },
  // Campaign milestone achievement
  {
    id: 'MILESTONE_CONTRIBUTOR',
    name: 'Milestone Maker',
    description: 'Participate in a campaign that reaches a milestone',
    icon: 'trophy',
    category: 'campaigns',
    threshold: 1,
    getProgress: async (userId) => {
      const result = await prisma.campaignEmail.findMany({
        where: {
          userId,
          campaign: { milestones: { some: {} } },
        },
        distinct: ['campaignId'],
        select: { campaignId: true },
      });
      return result.length;
    },
  },
  // Spotlight achievement
  {
    id: 'SPOTLIGHT_STAR',
    name: 'Spotlight Star',
    description: 'Be featured as a volunteer spotlight',
    icon: 'star',
    category: 'social',
    threshold: 1,
    getProgress: async (userId) =>
      prisma.volunteerSpotlight.count({
        where: { userId, status: 'FEATURED' },
      }),
  },
  // Challenge achievements
  {
    id: 'FIRST_CHALLENGE',
    name: 'Challenger',
    description: 'Join your first team challenge',
    icon: 'trophy',
    category: 'social',
    threshold: 1,
    getProgress: async (userId) =>
      prisma.challengeTeamMember.count({ where: { userId } }),
  },
  {
    id: 'CHALLENGE_WINNER',
    name: 'Champion',
    description: 'Win a team challenge',
    icon: 'crown',
    category: 'social',
    threshold: 1,
    getProgress: async (userId) => {
      // Count challenges where user's team has the highest score and challenge is COMPLETED
      const teams = await prisma.challengeTeamMember.findMany({
        where: { userId },
        include: {
          team: {
            include: {
              challenge: { select: { id: true, status: true } },
            },
          },
        },
      });
      let wins = 0;
      for (const membership of teams) {
        if (membership.team.challenge.status !== 'COMPLETED') continue;
        const topTeam = await prisma.challengeTeam.findFirst({
          where: { challengeId: membership.team.challenge.id },
          orderBy: { score: 'desc' },
          select: { id: true },
        });
        if (topTeam?.id === membership.teamId) wins++;
      }
      return wins;
    },
  },
];

/** Achievement map for quick lookup */
const ACHIEVEMENT_MAP = new Map(ACHIEVEMENTS.map((a) => [a.id, a]));

export const achievementsService = {
  /** Get all achievement definitions */
  getDefinitions() {
    return ACHIEVEMENTS.map(({ getProgress, ...rest }) => rest);
  },

  /** Get a user's achievements with progress */
  async listForUser(userId: string) {
    const unlocked = await prisma.userAchievement.findMany({
      where: { userId },
    });
    const unlockedMap = new Map(unlocked.map((u) => [u.achievementId, u]));

    const results = await Promise.all(
      ACHIEVEMENTS.map(async ({ getProgress, ...def }) => {
        const record = unlockedMap.get(def.id);
        let progress: number;

        if (record) {
          // Already unlocked — use stored progress (at least threshold)
          progress = Math.max(record.progress ?? def.threshold, def.threshold);
        } else {
          // Compute current progress
          progress = await getProgress(userId);
        }

        return {
          ...def,
          progress,
          unlocked: !!record,
          unlockedAt: record?.unlockedAt ?? null,
        };
      }),
    );

    return results;
  },

  /** Check and unlock achievements for a user after a specific event */
  async checkAndUnlock(userId: string, categories?: string[]) {
    const toCheck = categories
      ? ACHIEVEMENTS.filter((a) => categories.includes(a.category))
      : ACHIEVEMENTS;

    for (const achievement of toCheck) {
      try {
        // Skip if already unlocked
        const existing = await prisma.userAchievement.findUnique({
          where: { userId_achievementId: { userId, achievementId: achievement.id } },
        });
        if (existing) continue;

        const progress = await achievement.getProgress(userId);
        if (progress >= achievement.threshold) {
          await prisma.userAchievement.create({
            data: {
              userId,
              achievementId: achievement.id,
              unlockedAt: new Date(),
              progress,
              notified: false,
            },
          });

          // Create in-app notification
          await notificationService.createNotification(
            userId,
            'achievement',
            'Achievement Unlocked!',
            `You earned "${achievement.name}" — ${achievement.description}`,
            { achievementId: achievement.id, icon: achievement.icon },
          );

          logger.info(`Achievement unlocked: ${achievement.id} for user ${userId}`);
        }
      } catch (err) {
        logger.warn(`Failed to check achievement ${achievement.id} for ${userId}:`, err);
      }
    }
  },

  /** Get leaderboard by metric */
  async getLeaderboard(type: 'canvass' | 'shifts' | 'campaigns', limit = 10) {
    if (type === 'canvass') {
      // Rank by total canvass visits
      const results = await prisma.$queryRaw<{ userId: string; count: bigint }[]>`
        SELECT cs."userId", COUNT(cv.id) AS count
        FROM canvass_sessions cs
        JOIN canvass_visits cv ON cv."sessionId" = cs.id
        WHERE cs."userId" IS NOT NULL
        GROUP BY cs."userId"
        ORDER BY count DESC
        LIMIT ${limit}
      `;

      return this.hydrateLeaderboard(results.map((r) => ({
        userId: r.userId,
        score: Number(r.count),
      })));
    }

    if (type === 'shifts') {
      // Rank by total confirmed shift signups
      const results = await prisma.$queryRaw<{ userId: string; count: bigint }[]>`
        SELECT ss."userId", COUNT(ss.id) AS count
        FROM shift_signups ss
        WHERE ss."userId" IS NOT NULL AND ss.status = 'CONFIRMED'
        GROUP BY ss."userId"
        ORDER BY count DESC
        LIMIT ${limit}
      `;

      return this.hydrateLeaderboard(results.map((r) => ({
        userId: r.userId,
        score: Number(r.count),
      })));
    }

    // campaigns — rank by distinct campaigns participated
    const results = await prisma.$queryRaw<{ userId: string; count: bigint }[]>`
      SELECT ce."userId", COUNT(DISTINCT ce."campaignId") AS count
      FROM campaign_emails ce
      WHERE ce."userId" IS NOT NULL
      GROUP BY ce."userId"
      ORDER BY count DESC
      LIMIT ${limit}
    `;

    return this.hydrateLeaderboard(results.map((r) => ({
      userId: r.userId,
      score: Number(r.count),
    })));
  },

  /** Hydrate leaderboard entries with user data */
  async hydrateLeaderboard(entries: { userId: string; score: number }[]) {
    if (entries.length === 0) return [];

    // Filter out users who hide from activity feeds
    const hiddenIds = new Set(
      (await prisma.privacySettings.findMany({
        where: {
          userId: { in: entries.map((e) => e.userId) },
          showInFriendActivity: false,
        },
        select: { userId: true },
      })).map((p) => p.userId),
    );

    const visibleEntries = entries.filter((e) => !hiddenIds.has(e.userId));

    const users = await prisma.user.findMany({
      where: { id: { in: visibleEntries.map((e) => e.userId) } },
      select: { id: true, name: true, email: true },
    });
    const userMap = new Map(users.map((u) => [u.id, u]));

    return visibleEntries.map((e, i) => ({
      rank: i + 1,
      userId: e.userId,
      name: userMap.get(e.userId)?.name || null,
      email: userMap.get(e.userId)?.email || '',
      score: e.score,
    }));
  },

  /** Get a user's rank in a specific leaderboard */
  async getUserRank(userId: string, type: 'canvass' | 'shifts' | 'campaigns') {
    const leaderboard = await this.getLeaderboard(type, 1000);
    const idx = leaderboard.findIndex((e) => e.userId === userId);
    return idx >= 0 ? idx + 1 : null;
  },

  /** Get volunteer stats for a user (computed on-the-fly) */
  async getVolunteerStats(userId: string) {
    const user = await prisma.user.findUnique({ where: { id: userId }, select: { email: true } });
    if (!user) return null;

    const [shiftSignups, canvassSessions, canvassVisits, campaignEmails, distinctCampaigns, friendCount, groupCount] = await Promise.all([
      prisma.shiftSignup.count({
        where: { userEmail: user.email, status: SignupStatus.CONFIRMED },
      }),
      prisma.canvassSession.count({
        where: { userId, status: 'COMPLETED' },
      }),
      prisma.canvassVisit.count({
        where: { session: { userId } },
      }),
      prisma.campaignEmail.count({
        where: { userId },
      }),
      prisma.campaignEmail.findMany({
        where: { userId },
        distinct: ['campaignId'],
        select: { campaignId: true },
      }),
      prisma.friendship.count({
        where: { status: 'accepted', OR: [{ userId }, { friendId: userId }] },
      }),
      prisma.socialGroupMember.count({
        where: { userId },
      }),
    ]);

    return {
      shiftSignups,
      canvassSessions,
      canvassVisits,
      campaignEmails,
      campaignsParticipated: distinctCampaigns.length,
      friendCount,
      groupCount,
    };
  },
};
