import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { CrmPrismaService } from '../prisma/crm-prisma.service';
import { CreateCompanyDto } from './dto/create-company.dto';
import { UpdateCompanyDto } from './dto/update-company.dto';
import { QueryCompaniesDto } from './dto/query-companies.dto';
import { LexwareContactsService } from '../lexware/lexware-contacts.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 CompaniesService {
  private readonly logger = new Logger(CompaniesService.name);

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

  async create(tenantId: string, userId: string, dto: CreateCompanyDto) {
    // 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: 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;

    // Multi-Value emails vorbereiten
    const emailsCreate: Prisma.ContactEmailCreateWithoutCompanyInput[] = [];
    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.ContactPhoneCreateWithoutCompanyInput[] = [];
    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 });
    }

    return this.prisma.company.create({
      data: {
        tenantId,
        createdBy: userId,
        name: dto.name,
        industry: dto.industry,
        industryId: dto.industryId,
        accountTypeId: dto.accountTypeId,
        ownerId: dto.ownerId,
        ownerName: dto.ownerName,
        vatId: dto.vatId,
        taxId: dto.taxId,
        tradeRegisterNumber: dto.tradeRegisterNumber,
        registerCourt: dto.registerCourt,
        companySize: dto.companySize,
        email: primaryEmail,
        phone: primaryPhone,
        website: dto.website,
        street: dto.street,
        zip: dto.zip,
        city: dto.city,
        state: dto.state,
        country: dto.country,
        deliveryStreet: dto.deliveryStreet,
        deliveryZip: dto.deliveryZip,
        deliveryCity: dto.deliveryCity,
        deliveryCountry: dto.deliveryCountry,
        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: {
        industryRef: true,
        accountType: true,
        emails: true,
        phones: true,
        owners: true,
        _count: { select: { contacts: true, deals: true } },
      },
    });
  }

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

    const where: Prisma.CompanyWhereInput = { tenantId };

    if (query.industry) {
      where.industry = { contains: query.industry, mode: 'insensitive' };
    }

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

    if (query.search) {
      where.OR = [
        { name: { contains: query.search, mode: 'insensitive' } },
        { industry: { contains: query.search, mode: 'insensitive' } },
        { email: { contains: query.search, mode: 'insensitive' } },
        { city: { contains: query.search, mode: 'insensitive' } },
      ];
    }

    const allowedSortFields = [
      'createdAt',
      'updatedAt',
      'name',
      'industry',
      'city',
    ];
    const sortField = allowedSortFields.includes(query.sort ?? '')
      ? (query.sort as string)
      : 'createdAt';

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

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

  async findOne(tenantId: string, id: string) {
    const company = await this.prisma.company.findFirst({
      where: { id, tenantId },
      include: {
        industryRef: true,
        accountType: true,
        emails: true,
        phones: true,
        owners: true,
        contacts: {
          where: { status: 'ACTIVE' },
          orderBy: { createdAt: 'desc' },
          take: 20,
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            phone: true,
            position: true,
            isActive: true,
            status: true,
          },
        },
        deals: {
          orderBy: { createdAt: 'desc' },
          take: 10,
          include: {
            pipeline: { select: { id: true, name: true } },
            stage: { select: { id: true, name: true, color: true } },
          },
        },
        relationships: {
          include: {
            relatedCompany: {
              select: { id: true, name: true, industry: true, city: true },
            },
            relationshipType: {
              select: { id: true, name: true },
            },
          },
          orderBy: { createdAt: 'desc' },
        },
        relatedRelationships: {
          include: {
            company: {
              select: { id: true, name: true, industry: true, city: true },
            },
            relationshipType: {
              select: { id: true, name: true },
            },
          },
          orderBy: { createdAt: 'desc' },
        },
        contracts: {
          orderBy: { createdAt: 'desc' },
          take: 10,
        },
        _count: {
          select: { contacts: true, deals: true, lexwareVouchers: true, contracts: true },
        },
      },
    });

    if (!company) {
      throw new NotFoundException('Unternehmen nicht gefunden');
    }

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

    return { ...company, customFields };
  }

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

    // DTO-Felder aufteilen: Prisma-Scalar-Felder vs. nested Relations
    const { emails, phones, isActive, status: dtoStatus, ...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: { companyId: id } });
        if (emails.length > 0) {
          await tx.contactEmail.createMany({
            data: emails.map((e) => ({
              companyId: 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: { companyId: id } });
        if (phones.length > 0) {
          await tx.contactPhone.createMany({
            data: phones.map((p) => ({
              companyId: 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;

      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;
      }

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

      if (status !== undefined) updateData.status = status;
      if (isActiveBool !== undefined) updateData.isActive = isActiveBool;
      if (legacyEmail !== undefined) updateData.email = legacyEmail;
      if (legacyPhone !== undefined) updateData.phone = legacyPhone;

      return tx.company.update({
        where: { id },
        data: updateData,
        include: {
          industryRef: true,
          accountType: true,
          emails: true,
          phones: true,
          owners: true,
          _count: { select: { contacts: true, deals: true } },
        },
      });
    });

    // ERP-Push: Wenn Lexware verknuepft UND "ERP"-Tag gesetzt → async Push
    if (updated.lexwareContactId && updated.tags.includes('ERP')) {
      this.lexwareContacts
        .pushCompanyToLexware(tenantId, id)
        .catch((err: Error) =>
          this.logger.warn(
            `ERP-Push nach Update fehlgeschlagen fuer Company "${updated.name}": ${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.company.delete({ where: { id } });
  }
}
