import { prisma } from '../config/database';
import { logger } from '../utils/logger';
import type { ChallengeMetric } from '@prisma/client';

async function computeScore(
  userId: string,
  metric: ChallengeMetric,
  startsAt: Date,
  endsAt: Date,
): Promise<number> {
  const between = { gte: startsAt, lte: endsAt };

  switch (metric) {
    case 'DOORS_KNOCKED':
      return prisma.canvassVisit.count({
        where: {
          session: { userId },
          visitedAt: between,
        },
      });

    case 'EMAILS_SENT':
      return prisma.campaignEmail.count({
        where: {
          userId,
          sentAt: between,
        },
      });

    case 'SHIFTS_ATTENDED':
      return prisma.shiftSignup.count({
        where: {
          userId,
          status: 'CONFIRMED',
          shift: { startTime: { gte: startsAt.toISOString(), lte: endsAt.toISOString() } },
        },
      });

    case 'RESPONSES_SUBMITTED':
      return prisma.representativeResponse.count({
        where: {
          submittedByUserId: userId,
          createdAt: between,
        },
      });

    case 'REFERRALS_MADE':
      return prisma.referral.count({
        where: {
          referrerId: userId,
          completedAt: between,
        },
      });

    default:
      return 0;
  }
}

async function scoreChallenge(challengeId: string): Promise<void> {
  const challenge = await prisma.challenge.findUnique({
    where: { id: challengeId },
    include: {
      teams: {
        include: {
          members: { select: { id: true, userId: true, score: true } },
        },
      },
    },
  });

  if (!challenge) return;

  let anyChanged = false;
  const participantUserIds: string[] = [];

  for (const team of challenge.teams) {
    let teamTotal = 0;

    for (const member of team.members) {
      const newScore = await computeScore(
        member.userId,
        challenge.metric,
        challenge.startsAt,
        challenge.endsAt,
      );

      if (newScore !== member.score) {
        await prisma.challengeTeamMember.update({
          where: { id: member.id },
          data: { score: newScore },
        });
        anyChanged = true;
      }

      teamTotal += newScore;
      participantUserIds.push(member.userId);
    }

    if (teamTotal !== team.score) {
      await prisma.challengeTeam.update({
        where: { id: team.id },
        data: { score: teamTotal, lastScoredAt: new Date() },
      });
      anyChanged = true;
    }
  }

  if (anyChanged && participantUserIds.length > 0) {
    try {
      const { sseService } = await import('../modules/social/sse.service');
      sseService.sendToUsers(participantUserIds, 'challenge_scores_updated', {
        challengeId,
      });
    } catch {
      // SSE not available
    }
  }
}

async function processLifecycle(): Promise<void> {
  const now = new Date();

  // UPCOMING -> ACTIVE
  const toActivate = await prisma.challenge.findMany({
    where: { status: 'UPCOMING', startsAt: { lte: now } },
  });
  for (const c of toActivate) {
    await prisma.challenge.update({
      where: { id: c.id },
      data: { status: 'ACTIVE' },
    });
    logger.info(`Challenge ${c.id} "${c.title}" activated`);
  }

  // ACTIVE -> COMPLETED (past end date)
  const toComplete = await prisma.challenge.findMany({
    where: { status: 'ACTIVE', endsAt: { lte: now } },
  });
  for (const c of toComplete) {
    await scoreChallenge(c.id);
    await prisma.challenge.update({
      where: { id: c.id },
      data: { status: 'COMPLETED' },
    });
    logger.info(`Challenge ${c.id} "${c.title}" completed`);
  }

  // Score remaining ACTIVE challenges
  const active = await prisma.challenge.findMany({
    where: { status: 'ACTIVE' },
    select: { id: true },
  });
  for (const c of active) {
    await scoreChallenge(c.id);
  }

  logger.info(
    `Challenge lifecycle: ${toActivate.length} activated, ${toComplete.length} completed, ${active.length} active scored`,
  );
}

export const challengeScoringService = {
  computeScore,
  scoreChallenge,
  processLifecycle,
};
