import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { Prisma } from '@prisma/client';
import type {
  PartnerCreate,
  PartnerDto,
  PartnersResponse,
  PartnerUpdate,
} from '@siop/shared';
import { PrismaService } from '../prisma/prisma.service';

const partnerInclude = {
  _count: {
    select: {
      purchaseOrders: { where: { status: { in: ['DRAFT', 'SENT'] } } },
    },
  },
  // Rattachements de la maquette : sites (jamais les zones) gérés par un client/syndic.
  sites: {
    where: { parentId: null },
    select: { name: true },
    orderBy: { name: 'asc' as const },
  },
} satisfies Prisma.PartnerInclude;

type Row = Prisma.PartnerGetPayload<{ include: typeof partnerInclude }>;

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

  async list(): Promise<PartnersResponse> {
    const rows = await this.prisma.partner.findMany({
      include: partnerInclude,
      orderBy: [{ kind: 'asc' }, { name: 'asc' }],
    });
    return { partners: rows.map((r) => this.toDto(r)) };
  }

  async create(dto: PartnerCreate): Promise<PartnerDto> {
    try {
      const created = await this.prisma.partner.create({
        data: dto,
        include: partnerInclude,
      });
      return this.toDto(created);
    } catch (e) {
      if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
        throw new ConflictException('Ce nom de tiers existe déjà');
      }
      throw e;
    }
  }

  async update(id: string, dto: PartnerUpdate): Promise<PartnerDto> {
    try {
      const updated = await this.prisma.partner.update({
        where: { id },
        data: dto,
        include: partnerInclude,
      });
      return this.toDto(updated);
    } catch (e) {
      if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2025') {
        throw new NotFoundException('Tiers inconnu');
      }
      if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2002') {
        throw new ConflictException('Ce nom de tiers existe déjà');
      }
      throw e;
    }
  }

  private toDto(row: Row): PartnerDto {
    return {
      id: row.id,
      name: row.name,
      kind: row.kind,
      contactName: row.contactName,
      phone: row.phone,
      email: row.email,
      city: row.city,
      isActive: row.isActive,
      openOrders: row._count.purchaseOrders,
      siteNames: row.sites.map((s) => s.name),
    };
  }
}
