import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { CrmPrismaService } from '../prisma/crm-prisma.service';
import { CreateContactDto } from './dto/create-contact.dto';
import { UpdateContactDto } from './dto/update-contact.dto';
import { QueryContactsDto } from './dto/query-contacts.dto';
import { LexwareContactsService } from '../lexware/lexware-contacts.service';
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';
import { EntityStatus } from '../common/dto/contact-info.dto';

@Injectable()
export class ContactsService {
  private readonly logger = new Logger(ContactsService.name);

  constructor(
    private readonly prisma: CrmPrismaService,
    private readonly lexwareContacts: LexwareContactsService,
    private readonly eventPublisher: CrmEventPublisher,
    private readonly customFieldsService: CustomFieldsService,
  ) {}

  async create(tenantId: string, userId: string, dto: CreateContactDto) {
    // Status aus DTO ableiten (status hat Vorrang vor isActive)
    const status = dto.status
      ?? (dto.isActive === false ? EntityStatus.INACTIVE : EntityStatus.ACTIVE);
    const isActive = status !== EntityStatus.INACTIVE && status !== EntityStatus.BLOCKED;

    // Legacy email/phone/mobile: aus Multi-Value Primary ableiten oder aus DTO
    const primaryEmail = dto.emails?.find((e) => e.isPrimary)?.email
      ?? dto.emails?.[0]?.email ?? dto.email;
    const primaryPhone = dto.phones?.find((p) => p.isPrimary)?.phone
      ?? dto.phones?.find((p) => p.type === 'OFFICE')?.phone
      ?? dto.phones?.[0]?.phone ?? dto.phone;
    const primaryMobile = dto.phones?.find((p) => p.type === 'MOBILE')?.phone
      ?? dto.mobile;

    // Multi-Value emails vorbereiten
    const emailsCreate: Prisma.ContactEmailCreateWithoutContactInput[] = [];
    if (dto.emails && dto.emails.length > 0) {
      dto.emails.forEach((e) => {
        emailsCreate.push({
          email: e.email,
          type: e.type ?? 'WORK',
          isPrimary: e.isPrimary ?? false,
        });
      });
    } else if (dto.email) {
      emailsCreate.push({ email: dto.email, type: 'WORK', isPrimary: true });
    }

    // Multi-Value phones vorbereiten
    const phonesCreate: Prisma.ContactPhoneCreateWithoutContactInput[] = [];
    if (dto.phones && dto.phones.length > 0) {
      dto.phones.forEach((p) => {
        phonesCreate.push({
          phone: p.phone,
          type: p.type ?? 'OFFICE',
          isPrimary: p.isPrimary ?? false,
        });
      });
    } else {
      if (dto.phone) {
        phonesCreate.push({ phone: dto.phone, type: 'OFFICE', isPrimary: true });
      }
      if (dto.mobile) {
        phonesCreate.push({ phone: dto.mobile, type: 'MOBILE', isPrimary: false });
      }
    }

    const contact = await this.prisma.contact.create({
      data: {
        tenantId,
        createdBy: userId,
        type: dto.type,
        firstName: dto.firstName,
        lastName: dto.lastName,
        companyId: dto.companyId,
        companyName: dto.companyName,
        position: dto.position,
        email: primaryEmail,
        phone: primaryPhone,
        mobile: primaryMobile,
        website: dto.website,
        linkedinUrl: dto.linkedinUrl,
        birthday: dto.birthday ? new Date(dto.birthday) : undefined,
        source: dto.source,
        department: dto.department,
        street: dto.street,
        zip: dto.zip,
        city: dto.city,
        state: dto.state,
        country: dto.country,
        notes: dto.notes,
        tags: dto.tags ?? [],
        status,
        isActive,
        emails: emailsCreate.length > 0 ? { create: emailsCreate } : undefined,
        phones: phonesCreate.length > 0 ? { create: phonesCreate } : undefined,
        owners: {
          create: { tenantId, userId, role: 'OWNER' },
        },
      },
      include: {
        emails: true,
        phones: true,
        owners: true,
        company: { select: { id: true, name: true, industry: true } },
      },
    });

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

    return contact;
  }

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

    const where: Prisma.ContactWhereInput = { tenantId };

    if (query.type) {
      where.type = query.type;
    }

    if (query.companyId) {
      where.companyId = query.companyId;
    }

    if (query.status) {
      where.status = query.status;
    }

    if (query.search) {
      where.OR = [
        { firstName: { contains: query.search, mode: 'insensitive' } },
        { lastName: { contains: query.search, mode: 'insensitive' } },
        { companyName: { contains: query.search, mode: 'insensitive' } },
        { email: { contains: query.search, mode: 'insensitive' } },
      ];
    }

    const allowedSortFields = [
      'createdAt',
      'updatedAt',
      'firstName',
      'lastName',
      'companyName',
      'email',
    ];
    const sortField = allowedSortFields.includes(query.sort ?? '')
      ? (query.sort as string)
      : 'createdAt';

    const [data, total] = await Promise.all([
      this.prisma.contact.findMany({
        where,
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { [sortField]: query.order ?? 'desc' },
        include: {
          company: { select: { id: true, name: true, industry: true } },
          emails: true,
          phones: true,
        },
      }),
      this.prisma.contact.count({ where }),
    ]);

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

  /** Kontakt anhand einer E-Mail-Adresse suchen (erster Treffer) */
  async findByEmail(tenantId: string, email: string) {
    return this.prisma.contact.findFirst({
      where: {
        tenantId,
        OR: [
          { email: { equals: email, mode: 'insensitive' } },
          { emails: { some: { email: { equals: email, mode: 'insensitive' } } } },
        ],
      },
      select: {
        id: true,
        firstName: true,
        lastName: true,
        email: true,
        companyName: true,
      },
    });
  }

  async findOne(tenantId: string, id: string) {
    const contact = await this.prisma.contact.findFirst({
      where: { id, tenantId },
      include: {
        company: { select: { id: true, name: true, industry: true, city: true, website: true } },
        activities: { orderBy: { createdAt: 'desc' }, take: 10 },
        emails: true,
        phones: true,
        owners: true,
      },
    });

    if (!contact) {
      throw new NotFoundException('Kontakt nicht gefunden');
    }

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

    return { ...contact, customFields };
  }

  async update(
    tenantId: string,
    id: string,
    userId: string,
    dto: UpdateContactDto,
  ) {
    await this.findOne(tenantId, id);

    // DTO-Felder aufteilen: Prisma-Scalar-Felder vs. nested Relations
    const { emails, phones, isActive, status: dtoStatus, birthday, ...scalarFields } = dto;

    // Status ↔ isActive synchron halten
    let status: EntityStatus | undefined;
    let isActiveBool: boolean | undefined;
    if (dtoStatus !== undefined) {
      status = dtoStatus;
      isActiveBool = dtoStatus !== EntityStatus.INACTIVE && dtoStatus !== EntityStatus.BLOCKED;
    } else if (isActive !== undefined) {
      isActiveBool = isActive;
      status = isActive ? EntityStatus.ACTIVE : EntityStatus.INACTIVE;
    }

    // Replace-Strategie fuer emails/phones in Transaction
    const updated = await this.prisma.$transaction(async (tx) => {
      // Emails: Alle loeschen und neu anlegen
      if (emails !== undefined) {
        await tx.contactEmail.deleteMany({ where: { contactId: id } });
        if (emails.length > 0) {
          await tx.contactEmail.createMany({
            data: emails.map((e) => ({
              contactId: id,
              email: e.email,
              type: e.type ?? 'WORK',
              isPrimary: e.isPrimary ?? false,
            })),
          });
        }
      }

      // Phones: Alle loeschen und neu anlegen
      if (phones !== undefined) {
        await tx.contactPhone.deleteMany({ where: { contactId: id } });
        if (phones.length > 0) {
          await tx.contactPhone.createMany({
            data: phones.map((p) => ({
              contactId: id,
              phone: p.phone,
              type: p.type ?? 'OFFICE',
              isPrimary: p.isPrimary ?? false,
            })),
          });
        }
      }

      // Legacy-Felder aus Multi-Value aktualisieren
      let legacyEmail: string | null | undefined;
      let legacyPhone: string | null | undefined;
      let legacyMobile: string | null | undefined;

      if (emails !== undefined) {
        legacyEmail = emails.find((e) => e.isPrimary)?.email
          ?? emails[0]?.email ?? null;
      }
      if (phones !== undefined) {
        legacyPhone = phones.find((p) => p.isPrimary)?.phone
          ?? phones.find((p) => p.type === 'OFFICE')?.phone
          ?? phones[0]?.phone ?? null;
        legacyMobile = phones.find((p) => p.type === 'MOBILE')?.phone ?? null;
      }

      const updateData: Prisma.ContactUpdateInput = {
        ...scalarFields,
        updatedBy: userId,
      };

      if (birthday !== undefined) {
        updateData.birthday = birthday ? new Date(birthday) : null;
      }
      if (status !== undefined) updateData.status = status;
      if (isActiveBool !== undefined) updateData.isActive = isActiveBool;
      if (legacyEmail !== undefined) updateData.email = legacyEmail;
      if (legacyPhone !== undefined) updateData.phone = legacyPhone;
      if (legacyMobile !== undefined) updateData.mobile = legacyMobile;

      return tx.contact.update({
        where: { id },
        data: updateData,
        include: {
          emails: true,
          phones: true,
          owners: true,
          company: { select: { id: true, name: true, industry: true } },
        },
      });
    });

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

    // ERP-Push: Wenn Lexware verknuepft UND "ERP"-Tag gesetzt → async Push
    if (updated.lexwareContactId && updated.tags.includes('ERP')) {
      this.lexwareContacts
        .pushContactToLexware(tenantId, id)
        .catch((err: Error) =>
          this.logger.warn(
            `ERP-Push nach Update fehlgeschlagen fuer Contact "${updated.firstName} ${updated.lastName}": ${err.message}`,
          ),
        );
    }

    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.contact.delete({ where: { id } });
  }
}
