import { randomBytes } from 'crypto';
import { CampaignStatus, Prisma, ResponseStatus, UserRole } from '@prisma/client';
import { prisma } from '../../../config/database';
import { AppError } from '../../../middleware/error-handler';
import { emailService } from '../../../services/email.service';
import { notificationQueueService } from '../../../services/notification-queue.service';
import { getAdminEmailsByRole, isNotificationEnabled } from '../../../services/notification.helper';
import { env } from '../../../config/env';
import { logger } from '../../../utils/logger';
import { recordResponseSubmission } from '../../../utils/metrics';
import { eventBus } from '../../../services/event-bus.service';
import type {
  SubmitResponseInput,
  ListPublicResponsesInput,
  ListAdminResponsesInput,
  UpdateResponseStatusInput,
} from './responses.schemas';

const VERIFICATION_EXPIRY_DAYS = 30;

export const responsesService = {
  // --- Public ---

  async submitResponse(slug: string, data: SubmitResponseInput, senderIp?: string) {
    const campaign = await prisma.campaign.findUnique({
      where: { slug },
      select: { id: true, slug: true, title: true, status: true, showResponseWall: true },
    });

    if (!campaign) {
      throw new AppError(404, 'Campaign not found', 'CAMPAIGN_NOT_FOUND');
    }
    if (campaign.status !== CampaignStatus.ACTIVE) {
      throw new AppError(400, 'Campaign is not active', 'CAMPAIGN_NOT_ACTIVE');
    }
    if (!campaign.showResponseWall) {
      throw new AppError(400, 'Response wall is not enabled for this campaign', 'RESPONSE_WALL_DISABLED');
    }

    let verificationToken: string | null = null;
    let verificationSentAt: Date | null = null;

    if (data.sendVerification && data.representativeEmail) {
      verificationToken = randomBytes(32).toString('hex');
      verificationSentAt = new Date();
    }

    const response = await prisma.representativeResponse.create({
      data: {
        campaignId: campaign.id,
        campaignSlug: campaign.slug,
        representativeName: data.representativeName,
        representativeTitle: data.representativeTitle,
        representativeLevel: data.representativeLevel,
        representativeEmail: data.representativeEmail,
        responseType: data.responseType,
        responseText: data.responseText,
        userComment: data.userComment,
        submittedByName: data.submittedByName,
        submittedByEmail: data.submittedByEmail,
        isAnonymous: data.isAnonymous,
        status: ResponseStatus.PENDING,
        verificationToken,
        verificationSentAt,
        submittedIp: senderIp,
      },
    });

    if (verificationToken && data.representativeEmail) {
      const baseUrl = env.API_URL;
      await emailService.sendResponseVerification({
        recipientEmail: data.representativeEmail,
        campaignTitle: campaign.title,
        responseType: data.responseType,
        responseText: data.responseText,
        submitterName: data.isAnonymous ? 'Anonymous' : (data.submittedByName || 'Anonymous'),
        verificationUrl: `${baseUrl}/api/responses/${response.id}/verify/${verificationToken}`,
        reportUrl: `${baseUrl}/api/responses/${response.id}/report/${verificationToken}`,
      });
    }

    recordResponseSubmission();

    // Notification: admin response submitted alert
    try {
      if (await isNotificationEnabled('notifyAdminResponseSubmitted')) {
        const adminEmails = await getAdminEmailsByRole([UserRole.SUPER_ADMIN, UserRole.INFLUENCE_ADMIN]);
        if (adminEmails.length > 0) {
          const adminUrl = `${env.ADMIN_URL || 'http://localhost:3000'}/app/influence/responses`;
          await notificationQueueService.enqueue({
            type: 'admin-response-submitted',
            adminEmails,
            campaignTitle: campaign.title,
            representativeName: data.representativeName,
            responseType: data.responseType,
            submitterName: data.isAnonymous ? 'Anonymous' : (data.submittedByName || 'Anonymous'),
            adminUrl,
          });
        }
      }
    } catch (err) {
      logger.error('Failed to enqueue response submitted notification:', err);
    }

    // Publish response submitted event
    eventBus.publish('response.submitted', {
      responseId: response.id,
      campaignId: campaign.id,
      campaignTitle: campaign.title,
      representativeName: data.representativeName,
      userEmail: data.submittedByEmail,
    });

    return {
      id: response.id,
      status: response.status,
      verificationSent: !!verificationToken,
    };
  },

  async listApproved(slug: string, filters: ListPublicResponsesInput) {
    const { page, limit, sort, level } = filters;
    const skip = (page - 1) * limit;

    const where: Prisma.RepresentativeResponseWhereInput = {
      campaignSlug: slug,
      status: ResponseStatus.APPROVED,
    };
    if (level) where.representativeLevel = level;

    let orderBy: Prisma.RepresentativeResponseOrderByWithRelationInput;
    switch (sort) {
      case 'upvotes':
        orderBy = { upvoteCount: 'desc' };
        break;
      case 'verified':
        orderBy = { isVerified: 'desc' };
        break;
      default:
        orderBy = { createdAt: 'desc' };
    }

    const [responses, total] = await Promise.all([
      prisma.representativeResponse.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        // NOTE: `userComment` and `submittedByName` were removed from the public
        // select on 2026-04-12 after red-team audit found submitter identities were
        // being scraped. Admin moderation views (authenticated) still see them.
        select: {
          id: true,
          representativeName: true,
          representativeTitle: true,
          representativeLevel: true,
          responseType: true,
          responseText: true,
          isAnonymous: true,
          isVerified: true,
          verifiedAt: true,
          upvoteCount: true,
          createdAt: true,
        },
      }),
      prisma.representativeResponse.count({ where }),
    ]);

    return {
      responses,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    };
  },

  async getStats(slug: string) {
    const where = { campaignSlug: slug, status: ResponseStatus.APPROVED };

    const [total, verified, upvoteSum, byLevel] = await Promise.all([
      prisma.representativeResponse.count({ where }),
      prisma.representativeResponse.count({ where: { ...where, isVerified: true } }),
      prisma.representativeResponse.aggregate({ where, _sum: { upvoteCount: true } }),
      prisma.representativeResponse.groupBy({
        by: ['representativeLevel'],
        where,
        _count: true,
      }),
    ]);

    const levelBreakdown: Record<string, number> = {};
    for (const row of byLevel) {
      levelBreakdown[row.representativeLevel] = row._count;
    }

    return {
      total,
      verified,
      totalUpvotes: upvoteSum._sum.upvoteCount || 0,
      byLevel: levelBreakdown,
    };
  },

  async upvote(responseId: string, userIp?: string, userId?: string) {
    // Verify response exists and is approved
    const response = await prisma.representativeResponse.findUnique({
      where: { id: responseId },
      select: { id: true, status: true },
    });
    if (!response) throw new AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
    if (response.status !== ResponseStatus.APPROVED) throw new AppError(400, 'Response is not approved', 'RESPONSE_NOT_APPROVED');

    try {
      await prisma.responseUpvote.create({
        data: {
          responseId,
          userId: userId || null,
          upvotedIp: !userId ? (userIp || null) : null,
        },
      });

      await prisma.representativeResponse.update({
        where: { id: responseId },
        data: { upvoteCount: { increment: 1 } },
      });

      return { success: true };
    } catch (err: any) {
      if (err.code === 'P2002') {
        return { success: false, alreadyUpvoted: true };
      }
      throw err;
    }
  },

  async removeUpvote(responseId: string, userIp?: string, userId?: string) {
    const where: Prisma.ResponseUpvoteWhereInput = { responseId };
    if (userId) {
      where.userId = userId;
    } else if (userIp) {
      where.upvotedIp = userIp;
    } else {
      return { success: false };
    }

    const deleted = await prisma.responseUpvote.deleteMany({ where });

    if (deleted.count > 0) {
      await prisma.representativeResponse.update({
        where: { id: responseId },
        data: {
          upvoteCount: { decrement: 1 },
        },
      });
    }

    return { success: deleted.count > 0 };
  },

  async verify(responseId: string, token: string) {
    const response = await prisma.representativeResponse.findUnique({
      where: { id: responseId },
      select: {
        id: true,
        verificationToken: true,
        verificationSentAt: true,
        representativeEmail: true,
        campaign: { select: { title: true } },
      },
    });

    if (!response || response.verificationToken !== token) {
      return { success: false, reason: 'Invalid verification link' };
    }

    // Check 30-day expiry
    if (response.verificationSentAt) {
      const daysSinceSent = (Date.now() - response.verificationSentAt.getTime()) / (1000 * 60 * 60 * 24);
      if (daysSinceSent > VERIFICATION_EXPIRY_DAYS) {
        return { success: false, reason: 'Verification link has expired' };
      }
    }

    await prisma.representativeResponse.update({
      where: { id: responseId },
      data: {
        isVerified: true,
        verifiedAt: new Date(),
        verifiedBy: response.representativeEmail || 'Representative',
        status: ResponseStatus.APPROVED,
      },
    });

    return { success: true, campaignTitle: response.campaign.title };
  },

  async report(responseId: string, token: string) {
    const response = await prisma.representativeResponse.findUnique({
      where: { id: responseId },
      select: {
        id: true,
        verificationToken: true,
        representativeEmail: true,
        campaign: { select: { title: true } },
      },
    });

    if (!response || response.verificationToken !== token) {
      return { success: false, reason: 'Invalid verification link' };
    }

    await prisma.representativeResponse.update({
      where: { id: responseId },
      data: {
        status: ResponseStatus.REJECTED,
        isVerified: false,
        verifiedBy: `Disputed by ${response.representativeEmail || 'representative'}`,
      },
    });

    return { success: true, campaignTitle: response.campaign.title };
  },

  // --- Admin ---

  async findAll(filters: ListAdminResponsesInput) {
    const { page, limit, status, campaignId, search } = filters;
    const skip = (page - 1) * limit;

    const where: Prisma.RepresentativeResponseWhereInput = {};
    if (status) where.status = status;
    if (campaignId) where.campaignId = campaignId;
    if (search) {
      where.OR = [
        { representativeName: { contains: search, mode: 'insensitive' } },
        { responseText: { contains: search, mode: 'insensitive' } },
        { submittedByName: { contains: search, mode: 'insensitive' } },
      ];
    }

    const [responses, total] = await Promise.all([
      prisma.representativeResponse.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          representativeName: true,
          representativeTitle: true,
          representativeLevel: true,
          representativeEmail: true,
          responseType: true,
          responseText: true,
          userComment: true,
          submittedByName: true,
          submittedByEmail: true,
          isAnonymous: true,
          status: true,
          isVerified: true,
          verifiedAt: true,
          verifiedBy: true,
          upvoteCount: true,
          createdAt: true,
          campaign: { select: { id: true, title: true, slug: true } },
        },
      }),
      prisma.representativeResponse.count({ where }),
    ]);

    return {
      responses,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    };
  },

  async updateStatus(id: string, data: UpdateResponseStatusInput) {
    const existing = await prisma.representativeResponse.findUnique({ where: { id } });
    if (!existing) throw new AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');

    return prisma.representativeResponse.update({
      where: { id },
      data: { status: data.status },
    });
  },

  async deleteResponse(id: string) {
    const existing = await prisma.representativeResponse.findUnique({ where: { id } });
    if (!existing) throw new AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');

    await prisma.representativeResponse.delete({ where: { id } });
  },

  async resendVerification(id: string) {
    const response = await prisma.representativeResponse.findUnique({
      where: { id },
      select: {
        id: true,
        representativeEmail: true,
        representativeName: true,
        responseType: true,
        responseText: true,
        submittedByName: true,
        isAnonymous: true,
        verificationToken: true,
        campaign: { select: { title: true } },
      },
    });

    if (!response) throw new AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
    if (!response.representativeEmail) {
      throw new AppError(400, 'No representative email on record', 'NO_REPRESENTATIVE_EMAIL');
    }

    // Regenerate token
    const verificationToken = response.verificationToken || randomBytes(32).toString('hex');

    await prisma.representativeResponse.update({
      where: { id },
      data: {
        verificationToken,
        verificationSentAt: new Date(),
      },
    });

    const baseUrl = env.API_URL;
    await emailService.sendResponseVerification({
      recipientEmail: response.representativeEmail,
      campaignTitle: response.campaign.title,
      responseType: response.responseType,
      responseText: response.responseText,
      submitterName: response.isAnonymous ? 'Anonymous' : (response.submittedByName || 'Anonymous'),
      verificationUrl: `${baseUrl}/api/responses/${id}/verify/${verificationToken}`,
      reportUrl: `${baseUrl}/api/responses/${id}/report/${verificationToken}`,
    });

    return { success: true };
  },
};
