import { Injectable, NotFoundException } from '@nestjs/common';
// CrmActivityType used in method signatures via DTO typing
import { PrismaService } from '../../../prisma/prisma.service';
import { PaginatedResult } from '../core/entity-manager.base';
import { TimelineService } from './timeline.service';
import { CreateActivityDto } from '../dto/create-activity.dto';
import { CompleteActivityDto } from '../dto/complete-activity.dto';

const ACTIVITY_INCLUDE = {
  deal: {
    select: { id: true, title: true, contactName: true, contactPhone: true },
  },
  assignee: { select: { id: true, firstName: true, lastName: true } },
  completedBy: { select: { id: true, firstName: true, lastName: true } },
};

@Injectable()
export class ActivitiesService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly timeline: TimelineService,
  ) {}

  async findByDeal(dealId: string, cursor?: string, limit = 30): Promise<PaginatedResult<any>> {
    const take = Math.min(limit, 100);
    const items = await this.prisma.crmActivity.findMany({
      where: { dealId },
      take: take + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { scheduledAt: 'asc' },
      include: ACTIVITY_INCLUDE,
    });

    const hasMore = items.length > take;
    const data = hasMore ? items.slice(0, -1) : items;
    return {
      data,
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    };
  }

  async findByUser(
    userId: string,
    filters?: {
      dateFrom?: string;
      dateTo?: string;
      completed?: boolean;
    },
    cursor?: string,
    limit = 30,
  ): Promise<PaginatedResult<any>> {
    const take = Math.min(limit, 100);
    const where: Record<string, any> = { assigneeId: userId };

    if (filters?.completed !== undefined) {
      where.completedAt = filters.completed ? { not: null } : null;
    }
    if (filters?.dateFrom || filters?.dateTo) {
      where.scheduledAt = {
        ...(filters?.dateFrom ? { gte: new Date(filters.dateFrom) } : {}),
        ...(filters?.dateTo ? { lte: new Date(filters.dateTo) } : {}),
      };
    }

    const items = await this.prisma.crmActivity.findMany({
      where,
      take: take + 1,
      ...(cursor ? { cursor: { id: cursor }, skip: 1 } : {}),
      orderBy: { scheduledAt: 'asc' },
      include: ACTIVITY_INCLUDE,
    });

    const hasMore = items.length > take;
    const data = hasMore ? items.slice(0, -1) : items;
    return {
      data,
      nextCursor: hasMore ? data[data.length - 1].id : null,
      hasMore,
    };
  }

  async getById(id: string): Promise<any> {
    const activity = await this.prisma.crmActivity.findUnique({
      where: { id },
      include: ACTIVITY_INCLUDE,
    });
    if (!activity) throw new NotFoundException(`Activity ${id} not found`);
    return activity;
  }

  async create(dto: CreateActivityDto, userId: string): Promise<any> {
    const activity = await this.prisma.crmActivity.create({
      data: {
        dealId: dto.dealId,
        assigneeId: userId,
        type: dto.type,
        subject: dto.subject,
        description: dto.description,
        scheduledAt: new Date(dto.scheduledAt),
        deadline: dto.deadline ? new Date(dto.deadline) : undefined,
        priority: dto.priority,
        channel: dto.channel,
      },
      include: ACTIVITY_INCLUDE,
    });

    // Timeline entry
    if (dto.dealId) {
      await this.timeline.addEntry(dto.dealId, userId, 'ACTIVITY_CREATED', {
        subject: `Запланировано: ${activity.subject}`,
        metadata: {
          activityId: activity.id,
          activityType: activity.type,
          scheduledAt: activity.scheduledAt.toISOString(),
        },
      });
    }

    return activity;
  }

  async complete(id: string, dto: CompleteActivityDto, userId: string): Promise<any> {
    const activity = await this.getById(id);

    const updated = await this.prisma.crmActivity.update({
      where: { id },
      data: {
        completedAt: new Date(),
        completedById: userId,
        result: dto.result,
      },
      include: ACTIVITY_INCLUDE,
    });

    // Timeline entry
    if (activity.dealId) {
      await this.timeline.addEntry(activity.dealId, userId, 'ACTIVITY_DONE', {
        subject: `Выполнено: ${activity.subject}`,
        metadata: {
          activityId: activity.id,
          activityType: activity.type,
          result: dto.result,
        },
      });
    }

    return updated;
  }

  async update(id: string, data: Partial<CreateActivityDto>): Promise<any> {
    await this.getById(id); // ensure exists

    const updateData: Record<string, any> = {};
    if (data.type) updateData.type = data.type;
    if (data.subject) updateData.subject = data.subject;
    if (data.description !== undefined) updateData.description = data.description;
    if (data.scheduledAt) updateData.scheduledAt = new Date(data.scheduledAt);
    if (data.deadline !== undefined) {
      updateData.deadline = data.deadline ? new Date(data.deadline) : null;
    }
    if (data.priority) updateData.priority = data.priority;
    if (data.channel !== undefined) updateData.channel = data.channel;

    return this.prisma.crmActivity.update({
      where: { id },
      data: updateData,
      include: ACTIVITY_INCLUDE,
    });
  }

  async remove(id: string): Promise<void> {
    await this.getById(id); // ensure exists
    await this.prisma.crmActivity.delete({ where: { id } });
  }

  /** Count overdue activities for scheduler */
  async findOverdue(): Promise<any[]> {
    return this.prisma.crmActivity.findMany({
      where: {
        completedAt: null,
        scheduledAt: { lt: new Date() },
      },
      include: ACTIVITY_INCLUDE,
      take: 100,
    });
  }
}
