import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { CrmStageType, CrmActivityType } from '@prisma/client';
import { CreatePipelineDto } from '../dto/create-pipeline.dto';
import { UpdatePipelineDto } from '../dto/update-pipeline.dto';
import { CreateStageDto } from '../dto/create-stage.dto';
import { UpdateStageDto } from '../dto/update-stage.dto';
import { ReorderStagesDto } from '../dto/reorder-stages.dto';

const PIPELINE_INCLUDE = {
  stages: { orderBy: { position: 'asc' as const } },
};

interface DefaultStage {
  name: string;
  position: number;
  color: string;
  type: CrmStageType;
  autoActivityType?: CrmActivityType;
  autoActivityDays?: number;
  staleDays?: number;
  isDefault: boolean;
}

const B2B_STAGES: DefaultStage[] = [
  {
    name: 'Новый лид',
    position: 1,
    color: '#3B82F6',
    type: 'OPEN',
    autoActivityType: 'CALL',
    autoActivityDays: 0,
    staleDays: undefined,
    isDefault: true,
  },
  {
    name: 'Квалификация',
    position: 2,
    color: '#8B5CF6',
    type: 'OPEN',
    staleDays: 2,
    isDefault: false,
  },
  {
    name: 'Демонстрация',
    position: 3,
    color: '#F59E0B',
    type: 'OPEN',
    autoActivityType: 'MEETING',
    autoActivityDays: 0,
    isDefault: false,
  },
  {
    name: 'Коммерческое предложение',
    position: 4,
    color: '#EF4444',
    type: 'OPEN',
    staleDays: 3,
    isDefault: false,
  },
  {
    name: 'Согласование',
    position: 5,
    color: '#EC4899',
    type: 'OPEN',
    staleDays: 5,
    isDefault: false,
  },
  { name: 'Оплата', position: 6, color: '#14B8A6', type: 'OPEN', isDefault: false },
  { name: 'Выиграна', position: 7, color: '#22C55E', type: 'WON', isDefault: false },
  { name: 'Проиграна', position: 8, color: '#6B7280', type: 'LOST', isDefault: false },
];

const B2C_STAGES: DefaultStage[] = [
  {
    name: 'Обращение',
    position: 1,
    color: '#3B82F6',
    type: 'OPEN',
    autoActivityType: 'CALL',
    autoActivityDays: 0,
    isDefault: true,
  },
  { name: 'Первый контакт', position: 2, color: '#8B5CF6', type: 'OPEN', isDefault: false },
  {
    name: 'Пробное занятие',
    position: 3,
    color: '#F59E0B',
    type: 'OPEN',
    autoActivityType: 'MEETING',
    autoActivityDays: 0,
    isDefault: false,
  },
  {
    name: 'Предложение',
    position: 4,
    color: '#EF4444',
    type: 'OPEN',
    staleDays: 2,
    isDefault: false,
  },
  { name: 'Оплата абонемента', position: 5, color: '#14B8A6', type: 'OPEN', isDefault: false },
  { name: 'Продано', position: 6, color: '#22C55E', type: 'WON', isDefault: false },
  { name: 'Отказ', position: 7, color: '#6B7280', type: 'LOST', isDefault: false },
];

@Injectable()
export class PipelinesService {
  constructor(private readonly prisma: PrismaService) {}

  async findAll(clubId: string | null, entityType?: string): Promise<any[]> {
    return this.prisma.crmPipeline.findMany({
      where: {
        ...(clubId ? { clubId } : {}),
        ...(entityType ? { entityType } : {}),
      },
      include: PIPELINE_INCLUDE,
      orderBy: { createdAt: 'asc' },
    });
  }

  async findById(id: string): Promise<any> {
    const pipeline = await this.prisma.crmPipeline.findUnique({
      where: { id },
      include: PIPELINE_INCLUDE,
    });
    if (!pipeline) throw new NotFoundException(`Pipeline ${id} not found`);
    return pipeline;
  }

  async create(clubId: string | null, dto: CreatePipelineDto): Promise<any> {
    if (dto.isDefault) {
      await this.unsetDefaultPipeline(clubId);
    }
    return this.prisma.crmPipeline.create({
      data: { clubId, ...dto },
      include: PIPELINE_INCLUDE,
    });
  }

  async update(id: string, dto: UpdatePipelineDto): Promise<any> {
    const pipeline = await this.findById(id);
    if (dto.isDefault) {
      await this.unsetDefaultPipeline(pipeline.clubId);
    }
    return this.prisma.crmPipeline.update({
      where: { id },
      data: dto,
      include: PIPELINE_INCLUDE,
    });
  }

  // Stage CRUD

  async getStages(pipelineId: string): Promise<any[]> {
    await this.findById(pipelineId); // ensure exists
    return this.prisma.crmStage.findMany({
      where: { pipelineId },
      orderBy: { position: 'asc' },
    });
  }

  async createStage(pipelineId: string, dto: CreateStageDto): Promise<any> {
    await this.findById(pipelineId);
    if (dto.isDefault) {
      await this.unsetDefaultStage(pipelineId);
    }
    return this.prisma.crmStage.create({
      data: { pipelineId, ...dto },
    });
  }

  async updateStage(id: string, dto: UpdateStageDto): Promise<any> {
    const stage = await this.prisma.crmStage.findUnique({ where: { id } });
    if (!stage) throw new NotFoundException(`Stage ${id} not found`);
    if (dto.isDefault) {
      await this.unsetDefaultStage(stage.pipelineId);
    }
    return this.prisma.crmStage.update({
      where: { id },
      data: dto,
    });
  }

  async reorderStages(dto: ReorderStagesDto): Promise<any[]> {
    if (dto.stageIds.length === 0) {
      throw new BadRequestException('stageIds must not be empty');
    }
    const updates = dto.stageIds.map((id, index) =>
      this.prisma.crmStage.update({
        where: { id },
        data: { position: index + 1 },
      }),
    );
    return this.prisma.$transaction(updates);
  }

  async deleteStage(stageId: string, moveToStageId?: string): Promise<void> {
    const stage = await this.prisma.crmStage.findUnique({
      where: { id: stageId },
      include: { _count: { select: { deals: true } } },
    });
    if (!stage) throw new NotFoundException(`Stage ${stageId} not found`);

    // Count stages by type in the pipeline
    const typeCounts = await this.prisma.crmStage.groupBy({
      by: ['type'],
      where: { pipelineId: stage.pipelineId },
      _count: true,
    });
    const typeCount = typeCounts.find((t) => t.type === stage.type)?._count ?? 0;

    // Cannot delete the only WON or LOST stage
    if ((stage.type === 'WON' || stage.type === 'LOST') && typeCount <= 1) {
      throw new BadRequestException(`Нельзя удалить единственную стадию типа ${stage.type}`);
    }

    const dealCount = stage._count.deals;

    if (dealCount > 0) {
      if (!moveToStageId) {
        throw new BadRequestException(
          `Стадия содержит ${dealCount} сделок. Укажите moveToStageId для переноса.`,
        );
      }

      // Validate target stage exists in same pipeline
      const targetStage = await this.prisma.crmStage.findUnique({
        where: { id: moveToStageId },
      });
      if (!targetStage) {
        throw new NotFoundException(`Target stage ${moveToStageId} not found`);
      }
      if (targetStage.pipelineId !== stage.pipelineId) {
        throw new BadRequestException('Target stage must be in the same pipeline');
      }

      // Move deals and record history
      await this.prisma.$transaction([
        this.prisma.crmDeal.updateMany({
          where: { stageId },
          data: { stageId: moveToStageId },
        }),
        // Stage history toStageId will be SET NULL by FK onDelete
      ]);
    }

    // Delete stage and reorder remaining
    await this.prisma.crmStage.delete({ where: { id: stageId } });

    // Reorder remaining stages
    const remaining = await this.prisma.crmStage.findMany({
      where: { pipelineId: stage.pipelineId },
      orderBy: { position: 'asc' },
    });
    if (remaining.length > 0) {
      await this.prisma.$transaction(
        remaining.map((s, i) =>
          this.prisma.crmStage.update({
            where: { id: s.id },
            data: { position: i + 1 },
          }),
        ),
      );
    }
  }

  async deletePipeline(id: string): Promise<void> {
    const pipeline = await this.prisma.crmPipeline.findUnique({
      where: { id },
      include: { _count: { select: { deals: true } } },
    });
    if (!pipeline) throw new NotFoundException(`Pipeline ${id} not found`);

    if (pipeline._count.deals > 0) {
      throw new BadRequestException(
        `Воронка содержит ${pipeline._count.deals} сделок. Удалите или перенесите сделки.`,
      );
    }

    // Delete stages first, then pipeline
    await this.prisma.$transaction([
      this.prisma.crmStage.deleteMany({ where: { pipelineId: id } }),
      this.prisma.crmPipeline.delete({ where: { id } }),
    ]);
  }

  // Default pipeline creation

  async createDefaultPipeline(clubId: string | null, type: 'b2b' | 'b2c'): Promise<any> {
    const stages = type === 'b2b' ? B2B_STAGES : B2C_STAGES;
    const name = type === 'b2b' ? 'Продажи B2B' : 'Продажи клиентам';

    return this.prisma.crmPipeline.create({
      data: {
        clubId,
        name,
        isDefault: true,
        stages: {
          create: stages,
        },
      },
      include: PIPELINE_INCLUDE,
    });
  }

  /** Get the default stage (isDefault=true) for a pipeline */
  async getDefaultStage(pipelineId: string): Promise<any> {
    const stage = await this.prisma.crmStage.findFirst({
      where: { pipelineId, isDefault: true },
    });
    if (!stage) {
      // Fallback: first OPEN stage
      return this.prisma.crmStage.findFirst({
        where: { pipelineId, type: 'OPEN' },
        orderBy: { position: 'asc' },
      });
    }
    return stage;
  }

  /** Get the default pipeline for a club (or platform), optionally scoped by entityType */
  async getDefaultPipeline(clubId: string | null, entityType = 'deal'): Promise<any> {
    const pipeline = await this.prisma.crmPipeline.findFirst({
      where: { clubId, entityType, isDefault: true, isActive: true },
      include: PIPELINE_INCLUDE,
    });
    return pipeline;
  }

  private async unsetDefaultPipeline(clubId: string | null): Promise<void> {
    await this.prisma.crmPipeline.updateMany({
      where: { clubId, isDefault: true },
      data: { isDefault: false },
    });
  }

  private async unsetDefaultStage(pipelineId: string): Promise<void> {
    await this.prisma.crmStage.updateMany({
      where: { pipelineId, isDefault: true },
      data: { isDefault: false },
    });
  }
}
