import { prisma } from '../../config/database';
import { ImpactStoryStatus, type Prisma } from '@prisma/client';
import { AppError } from '../../middleware/error-handler';
import { notificationService } from './notification.service';
import { logger } from '../../utils/logger';
import type { CreateStoryInput, UpdateStoryInput } from './impact-stories.schemas';

const MILESTONE_THRESHOLDS = [50, 100, 250, 500, 1000, 2500, 5000];

export const impactStoriesService = {
  async create(data: CreateStoryInput, userId: string) {
    // Verify campaign exists
    const campaign = await prisma.campaign.findUnique({
      where: { id: data.campaignId },
      select: { id: true },
    });
    if (!campaign) {
      throw new AppError(404, 'Campaign not found', 'CAMPAIGN_NOT_FOUND');
    }

    return prisma.impactStory.create({
      data: {
        campaignId: data.campaignId,
        type: data.type,
        title: data.title,
        body: data.body,
        coverImageUrl: data.coverImageUrl,
        milestoneValue: data.milestoneValue,
        milestoneMetric: data.milestoneMetric,
        createdByUserId: userId,
      },
      include: {
        campaign: { select: { title: true } },
        createdBy: { select: { name: true, email: true } },
      },
    });
  },

  async update(id: string, data: UpdateStoryInput) {
    const existing = await prisma.impactStory.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }

    return prisma.impactStory.update({
      where: { id },
      data: {
        ...(data.campaignId !== undefined && { campaignId: data.campaignId }),
        ...(data.type !== undefined && { type: data.type }),
        ...(data.title !== undefined && { title: data.title }),
        ...(data.body !== undefined && { body: data.body }),
        ...(data.coverImageUrl !== undefined && { coverImageUrl: data.coverImageUrl }),
        ...(data.milestoneValue !== undefined && { milestoneValue: data.milestoneValue }),
        ...(data.milestoneMetric !== undefined && { milestoneMetric: data.milestoneMetric }),
      },
      include: {
        campaign: { select: { title: true } },
        createdBy: { select: { name: true, email: true } },
      },
    });
  },

  async delete(id: string) {
    const existing = await prisma.impactStory.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }
    await prisma.impactStory.delete({ where: { id } });
    return { success: true };
  },

  async publish(id: string) {
    const existing = await prisma.impactStory.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }

    return prisma.impactStory.update({
      where: { id },
      data: {
        status: ImpactStoryStatus.PUBLISHED,
        publishedAt: new Date(),
      },
      include: {
        campaign: { select: { title: true } },
        createdBy: { select: { name: true, email: true } },
      },
    });
  },

  async archive(id: string) {
    const existing = await prisma.impactStory.findUnique({ where: { id } });
    if (!existing) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }

    return prisma.impactStory.update({
      where: { id },
      data: { status: ImpactStoryStatus.ARCHIVED },
      include: {
        campaign: { select: { title: true } },
        createdBy: { select: { name: true, email: true } },
      },
    });
  },

  async findById(id: string) {
    const story = await prisma.impactStory.findUnique({
      where: { id },
      include: {
        campaign: { select: { title: true } },
        createdBy: { select: { name: true, email: true } },
      },
    });
    if (!story) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }
    return story;
  },

  async listByCampaign(campaignId: string, page: number, limit: number) {
    const skip = (page - 1) * limit;
    const where: Prisma.ImpactStoryWhereInput = { campaignId };

    const [stories, total] = await Promise.all([
      prisma.impactStory.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        include: {
          campaign: { select: { title: true } },
          createdBy: { select: { name: true, email: true } },
        },
      }),
      prisma.impactStory.count({ where }),
    ]);

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

  async listPublished(page: number, limit: number) {
    const skip = (page - 1) * limit;
    const where: Prisma.ImpactStoryWhereInput = { status: ImpactStoryStatus.PUBLISHED };

    const [stories, total] = await Promise.all([
      prisma.impactStory.findMany({
        where,
        skip,
        take: limit,
        orderBy: { publishedAt: 'desc' },
        include: {
          campaign: { select: { title: true } },
          createdBy: { select: { name: true, email: true } },
        },
      }),
      prisma.impactStory.count({ where }),
    ]);

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

  async listAdmin(page: number, limit: number, campaignId?: string, status?: string) {
    const skip = (page - 1) * limit;
    const where: Prisma.ImpactStoryWhereInput = {};
    if (campaignId) where.campaignId = campaignId;
    if (status) where.status = status as ImpactStoryStatus;

    const [stories, total] = await Promise.all([
      prisma.impactStory.findMany({
        where,
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        include: {
          campaign: { select: { title: true } },
          createdBy: { select: { name: true, email: true } },
        },
      }),
      prisma.impactStory.count({ where }),
    ]);

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

  async checkMilestones(campaignId: string) {
    try {
      // Count total emails for this campaign
      const emailCount = await prisma.campaignEmail.count({
        where: { campaignId },
      });

      // Get campaign info for auto-generated story titles
      const campaign = await prisma.campaign.findUnique({
        where: { id: campaignId },
        select: { id: true, title: true, createdByUserId: true },
      });
      if (!campaign) return;

      for (const threshold of MILESTONE_THRESHOLDS) {
        if (emailCount < threshold) break; // Thresholds are sorted, no point checking higher

        // Upsert milestone using the @@unique constraint
        const milestone = await prisma.campaignMilestone.upsert({
          where: {
            campaignId_metric_threshold: {
              campaignId,
              metric: 'emails_sent',
              threshold,
            },
          },
          update: {},
          create: {
            campaignId,
            metric: 'emails_sent',
            threshold,
            reachedAt: new Date(),
            storyGenerated: false,
          },
        });

        // If milestone exists but no story generated yet, create a draft
        if (!milestone.storyGenerated) {
          await prisma.$transaction(async (tx) => {
            await tx.impactStory.create({
              data: {
                campaignId,
                type: 'MILESTONE',
                title: `${campaign.title} reached ${threshold} emails!`,
                body: `A major milestone has been reached! The campaign "${campaign.title}" has now received ${threshold} advocacy emails from supporters.`,
                milestoneValue: threshold,
                milestoneMetric: 'emails_sent',
                createdByUserId: campaign.createdByUserId,
              },
            });

            await tx.campaignMilestone.update({
              where: { id: milestone.id },
              data: { storyGenerated: true },
            });
          });

          // Notify campaign creator
          if (campaign.createdByUserId) {
            notificationService.createNotification(
              campaign.createdByUserId,
              'system',
              'Campaign Milestone Reached!',
              `"${campaign.title}" has reached ${threshold} emails!`,
              { campaignId, milestone: threshold, type: 'campaign_milestone' },
            ).catch(() => {});
          }

          logger.info(`Milestone ${threshold} reached for campaign ${campaignId}`);
        }
      }
    } catch (err) {
      logger.warn(`Failed to check milestones for campaign ${campaignId}:`, err);
    }
  },

  async notifyParticipants(storyId: string) {
    const story = await prisma.impactStory.findUnique({
      where: { id: storyId },
      include: { campaign: { select: { id: true, title: true } } },
    });
    if (!story) {
      throw new AppError(404, 'Story not found', 'STORY_NOT_FOUND');
    }

    // Get unique user IDs from campaign emails (only registered users)
    const participants = await prisma.campaignEmail.findMany({
      where: { campaignId: story.campaignId, userId: { not: null } },
      distinct: ['userId'],
      select: { userId: true },
      take: 100,
    });

    let notified = 0;
    for (const p of participants) {
      if (!p.userId) continue;
      try {
        await notificationService.createNotification(
          p.userId,
          'system',
          story.title,
          `New update for "${story.campaign.title}": ${story.title}`,
          { storyId: story.id, campaignId: story.campaignId, type: 'impact_story' },
        );
        notified++;
      } catch {
        // Skip individual notification failures
      }
    }

    logger.info(`Notified ${notified} participants about story ${storyId}`);
    return { notified };
  },
};
