import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { CrmPrismaService } from '../prisma/crm-prisma.service';
import { CreateDealDto } from './dto/create-deal.dto';
import { UpdateDealDto } from './dto/update-deal.dto';
import { QueryDealsDto } from './dto/query-deals.dto';
import { CrmEventPublisher } from '../events/crm-event-publisher.service';
import { CustomFieldsService } from '../custom-fields/custom-fields.service';
import { CustomFieldEntityType } from '../custom-fields/dto/create-custom-field.dto';
import { Prisma } from '.prisma/crm-client';

@Injectable()
export class DealsService {
  constructor(
    private readonly prisma: CrmPrismaService,
    private readonly eventPublisher: CrmEventPublisher,
    private readonly customFieldsService: CustomFieldsService,
  ) {}

  async create(tenantId: string, userId: string, dto: CreateDealDto) {
    // Pipeline und Stage validieren
    const pipeline = await this.prisma.pipeline.findFirst({
      where: { id: dto.pipelineId, tenantId },
    });
    if (!pipeline) {
      throw new NotFoundException('Pipeline nicht gefunden');
    }

    const stage = await this.prisma.pipelineStage.findFirst({
      where: { id: dto.stageId, pipelineId: dto.pipelineId },
    });
    if (!stage) {
      throw new NotFoundException('Pipeline-Stufe nicht gefunden');
    }

    // Kontakt validieren (optional)
    if (dto.contactId) {
      const contact = await this.prisma.contact.findFirst({
        where: { id: dto.contactId, tenantId },
      });
      if (!contact) {
        throw new NotFoundException('Kontakt nicht gefunden');
      }
    }

    // Unternehmen validieren (optional)
    if (dto.companyId) {
      const company = await this.prisma.company.findFirst({
        where: { id: dto.companyId, tenantId },
      });
      if (!company) {
        throw new NotFoundException('Unternehmen nicht gefunden');
      }
    }

    const deal = await this.prisma.deal.create({
      data: {
        tenantId,
        pipelineId: dto.pipelineId,
        stageId: dto.stageId,
        contactId: dto.contactId,
        companyId: dto.companyId,
        title: dto.title,
        value: dto.value,
        currency: dto.currency ?? 'EUR',
        status: dto.status ?? 'OPEN',
        expectedCloseDate: dto.expectedCloseDate
          ? new Date(dto.expectedCloseDate)
          : undefined,
        notes: dto.notes,
        lostReason: dto.lostReason,
        lostReasonText: dto.lostReasonText,
        createdBy: userId,
        owners: {
          create: { tenantId, userId, role: 'OWNER' },
        },
      },
      include: {
        pipeline: { select: { id: true, name: true } },
        stage: { select: { id: true, name: true, color: true } },
        contact: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            companyName: true,
          },
        },
        company: {
          select: {
            id: true,
            name: true,
          },
        },
        owners: true,
      },
    });

    // Event publizieren (async, nicht blockierend)
    this.eventPublisher.dealCreated(tenantId, deal.id, userId).catch(() => {});

    return deal;
  }

  async findAll(tenantId: string, query: QueryDealsDto) {
    const page = query.page ?? 1;
    const pageSize = query.pageSize ?? 25;

    const where: Prisma.DealWhereInput = { tenantId };

    if (query.pipelineId) {
      where.pipelineId = query.pipelineId;
    }
    if (query.stageId) {
      where.stageId = query.stageId;
    }
    if (query.contactId) {
      where.contactId = query.contactId;
    }
    if (query.companyId) {
      where.companyId = query.companyId;
    }
    if (query.status) {
      where.status = query.status;
    }
    if (query.search) {
      where.title = { contains: query.search, mode: 'insensitive' };
    }

    const allowedSortFields = [
      'createdAt',
      'updatedAt',
      'title',
      'value',
      'expectedCloseDate',
    ];
    const sortField = allowedSortFields.includes(query.sort ?? '')
      ? (query.sort as string)
      : 'createdAt';

    const [data, total] = await Promise.all([
      this.prisma.deal.findMany({
        where,
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { [sortField]: query.order ?? 'desc' },
        include: {
          pipeline: { select: { id: true, name: true } },
          stage: { select: { id: true, name: true, color: true } },
          contact: {
            select: {
              id: true,
              firstName: true,
              lastName: true,
              companyName: true,
            },
          },
          company: {
            select: {
              id: true,
              name: true,
            },
          },
          owners: true,
        },
      }),
      this.prisma.deal.count({ where }),
    ]);

    return { data, total, page, pageSize };
  }

  async findOne(tenantId: string, id: string) {
    const deal = await this.prisma.deal.findFirst({
      where: { id, tenantId },
      include: {
        pipeline: { include: { stages: { orderBy: { sortOrder: 'asc' } } } },
        stage: true,
        contact: true,
        company: true,
        owners: true,
        dealVouchers: {
          include: {
            voucher: {
              select: {
                id: true,
                voucherType: true,
                voucherNumber: true,
                voucherDate: true,
                voucherStatus: true,
                totalGrossAmount: true,
                currency: true,
                title: true,
                lexwareDeepLink: true,
              },
            },
          },
          orderBy: { linkedAt: 'desc' },
        },
      },
    });

    if (!deal) {
      throw new NotFoundException('Vorgang nicht gefunden');
    }

    // Custom Fields anhaengen
    const customFields = await this.customFieldsService.getCustomFieldsForEntity(
      tenantId,
      CustomFieldEntityType.DEAL,
      id,
    );

    return { ...deal, customFields };
  }

  async update(
    tenantId: string,
    id: string,
    userId: string,
    dto: UpdateDealDto,
  ) {
    const existing = await this.findOne(tenantId, id);

    // Stage validieren wenn geaendert
    if (dto.stageId) {
      const pipelineId = dto.pipelineId ?? existing.pipelineId;
      const stage = await this.prisma.pipelineStage.findFirst({
        where: { id: dto.stageId, pipelineId },
      });
      if (!stage) {
        throw new NotFoundException('Pipeline-Stufe nicht gefunden');
      }
    }

    // Lost-Reason Validation
    if (dto.status === 'LOST') {
      // Wenn LOST gesetzt wird, muss lostReason vorhanden sein (im DTO oder bereits gesetzt)
      if (!dto.lostReason && !existing.lostReason) {
        throw new BadRequestException(
          'Verlustgrund (lostReason) ist Pflicht wenn Deal auf LOST gesetzt wird',
        );
      }
    }

    const { lostReason, lostReasonText, ...restDto } = dto;

    const updateData: Prisma.DealUpdateInput = {
      ...restDto,
      expectedCloseDate: dto.expectedCloseDate
        ? new Date(dto.expectedCloseDate)
        : undefined,
      updatedBy: userId,
    };

    // Lost-Reason Felder setzen
    if (lostReason !== undefined) {
      updateData.lostReason = lostReason;
    }
    if (lostReasonText !== undefined) {
      updateData.lostReasonText = lostReasonText;
    }

    // Wenn Deal gewonnen → lostReason und lostReasonText loeschen
    if (dto.status === 'WON') {
      updateData.lostReason = null;
      updateData.lostReasonText = null;
    }

    // Wenn Deal gewonnen/verloren, closedAt setzen
    if (dto.status === 'WON' || dto.status === 'LOST') {
      updateData.closedAt = new Date();
    }

    const updated = await this.prisma.deal.update({
      where: { id },
      data: updateData,
      include: {
        pipeline: { select: { id: true, name: true } },
        stage: { select: { id: true, name: true, color: true } },
        contact: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            companyName: true,
          },
        },
        company: {
          select: {
            id: true,
            name: true,
          },
        },
        owners: true,
      },
    });

    // Events publizieren (async, nicht blockierend)
    if (dto.stageId && dto.stageId !== existing.stageId) {
      this.eventPublisher.dealStageChanged(tenantId, id, userId, {
        previousStageId: existing.stageId,
        newStageId: dto.stageId,
      }).catch(() => {});
    }

    if (dto.status === 'WON') {
      this.eventPublisher.dealWon(tenantId, id, userId, {
        value: updated.value?.toNumber() ?? null,
        currency: updated.currency ?? 'EUR',
      }).catch(() => {});
    }

    if (dto.status === 'LOST') {
      this.eventPublisher.dealLost(tenantId, id, userId, {
        lostReason: updated.lostReason ?? null,
      }).catch(() => {});
    }

    return updated;
  }

  async remove(tenantId: string, id: string) {
    await this.findOne(tenantId, id);

    // Custom Field Values entfernen (entityId hat keinen FK, daher manuell)
    await this.prisma.customFieldValue.deleteMany({
      where: { tenantId, entityId: id },
    });

    return this.prisma.deal.delete({ where: { id } });
  }
}
