// ============================================================
// ENTITY: Product
// ============================================================
import {
  Entity, PrimaryGeneratedColumn, Column, CreateDateColumn,
  UpdateDateColumn, OneToMany, ManyToOne, JoinColumn,
} from 'typeorm';

@Entity('products')
export class Product {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ name: 'tenant_id' })
  tenantId: string;

  @Column({ length: 150 })
  name: string;

  @Column({ type: 'text', nullable: true })
  description: string;

  // Fiscal (ABRASF)
  @Column({ name: 'service_list_item', length: 5, nullable: true })
  serviceListItem: string;      // ex: "01.01"

  @Column({ name: 'cnae_code', length: 15, nullable: true })
  cnaeCode: string;

  @Column({ name: 'iss_aliquota', type: 'numeric', precision: 4, scale: 2, nullable: true })
  issAliquota: number;

  @Column({ name: 'iss_exigibility', type: 'smallint', default: 1 })
  issExigibility: number;

  @Column({ name: 'iss_retained', default: false })
  issRetained: boolean;

  @Column({ name: 'service_city_code', length: 7, nullable: true })
  serviceCityCode: string;

  @Column({ type: 'text', nullable: true })
  discrimination: string;

  @Column({ name: 'contract_template', type: 'text', nullable: true })
  contractTemplate: string;

  @Column({ default: 'active' })
  status: string;

  @OneToMany(() => ProductVariant, (v) => v.product, { cascade: true, eager: true })
  variants: ProductVariant[];

  @CreateDateColumn({ name: 'created_at' })
  createdAt: Date;

  @UpdateDateColumn({ name: 'updated_at' })
  updatedAt: Date;
}

// ============================================================
// ENTITY: ProductVariant
// ============================================================
@Entity('product_variants')
export class ProductVariant {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ name: 'product_id' })
  productId: string;

  @Column({ name: 'tenant_id' })
  tenantId: string;

  @Column({ length: 80 })
  name: string;                 // ex: "Plano Mensal"

  @Column({ type: 'numeric', precision: 15, scale: 2 })
  price: number;

  @Column({ name: 'billing_cycle', default: 'monthly' })
  billingCycle: string;         // monthly | yearly | once | hourly

  @Column({ name: 'setup_fee', type: 'numeric', precision: 15, scale: 2, default: 0 })
  setupFee: number;

  @Column({ type: 'text', nullable: true })
  description: string;

  @Column({ name: 'is_default', default: false })
  isDefault: boolean;

  @Column({ default: 'active' })
  status: string;

  @ManyToOne(() => Product, (p) => p.variants, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'product_id' })
  product: Product;

  @CreateDateColumn({ name: 'created_at' })
  createdAt: Date;

  @UpdateDateColumn({ name: 'updated_at' })
  updatedAt: Date;
}

// ============================================================
// DTO: CreateProduct
// ============================================================
import {
  IsString, IsOptional, IsNumber, IsBoolean,
  IsArray, ValidateNested, IsIn, MinLength, Min, Max,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class CreateProductVariantDto {
  @ApiPropertyOptional({ description: 'Presente ao editar uma variação existente; ausente cria uma nova' })
  @IsOptional()
  @IsString()
  id?: string;

  @ApiProperty({ example: 'Plano Mensal' })
  @IsString()
  @MinLength(2)
  name: string;

  @ApiProperty({ example: 1500.00 })
  @IsNumber()
  @Min(0)
  price: number;

  @ApiProperty({ enum: ['monthly', 'yearly', 'once', 'hourly'], default: 'monthly' })
  @IsIn(['monthly', 'yearly', 'once', 'hourly'])
  billingCycle: string;

  @ApiPropertyOptional({ example: 500.00 })
  @IsOptional()
  @IsNumber()
  @Min(0)
  setupFee?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  description?: string;

  @ApiPropertyOptional({ default: false })
  @IsOptional()
  @IsBoolean()
  isDefault?: boolean;
}

export class CreateProductDto {
  @ApiProperty({ example: 'Consultoria Jurídica' })
  @IsString()
  @MinLength(2)
  name: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  description?: string;

  // Campos fiscais
  @ApiPropertyOptional({ example: '01.01', description: 'Item da Lista de Serviços LC 116/03' })
  @IsOptional()
  @IsString()
  serviceListItem?: string;

  @ApiPropertyOptional({ example: '6911100', description: 'Código CNAE de 7 dígitos' })
  @IsOptional()
  @IsString()
  cnaeCode?: string;

  @ApiPropertyOptional({ example: 5.00, description: 'Alíquota ISS em % (ex: 5.00 = 5%)' })
  @IsOptional()
  @IsNumber()
  @Min(0)
  @Max(100)
  issAliquota?: number;

  @ApiPropertyOptional({ enum: [1,2,3,4,5,6,7], default: 1 })
  @IsOptional()
  @IsNumber()
  issExigibility?: number;

  @ApiPropertyOptional({ default: false })
  @IsOptional()
  @IsBoolean()
  issRetained?: boolean;

  @ApiPropertyOptional({ example: '3550308', description: 'Código IBGE da cidade de prestação' })
  @IsOptional()
  @IsString()
  serviceCityCode?: string;

  @ApiPropertyOptional({ description: 'Modelo de discriminação para NFS-e' })
  @IsOptional()
  @IsString()
  discrimination?: string;

  @ApiPropertyOptional({ description: 'Template do contrato com mnemônicos, ex: {{NOME_CLIENTE}}' })
  @IsOptional()
  @IsString()
  contractTemplate?: string;

  @ApiProperty({ type: [CreateProductVariantDto] })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => CreateProductVariantDto)
  variants: CreateProductVariantDto[];
}

export class UpdateProductDto extends CreateProductDto {}

// ============================================================
// SERVICE: ProductsService
// ============================================================
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private readonly productRepo: Repository<Product>,

    @InjectRepository(ProductVariant)
    private readonly variantRepo: Repository<ProductVariant>,

    private readonly dataSource: DataSource,
  ) {}

  async create(tenantId: string, dto: CreateProductDto): Promise<Product> {
    const product = this.productRepo.create({
      ...dto,
      tenantId,
      variants: dto.variants.map((v) =>
        this.variantRepo.create({ ...v, tenantId }),
      ),
    });
    return this.productRepo.save(product);
  }

  async findAll(tenantId: string, status?: string): Promise<(Product & { isUsed: boolean })[]> {
    const where: any = { tenantId };
    if (status) where.status = status;
    const products = await this.productRepo.find({ where, relations: ['variants'], order: { name: 'ASC' } });

    if (products.length === 0) return [];

    const ids = products.map((p) => p.id);
    const usedRows: { product_id: string }[] = await this.dataSource.query(
      `SELECT DISTINCT product_id FROM proposal_items WHERE product_id = ANY($1)`,
      [ids],
    );
    const usedSet = new Set(usedRows.map((r) => r.product_id));

    return products.map((p) => Object.assign(p, { isUsed: usedSet.has(p.id) }));
  }

  async findOne(tenantId: string, id: string): Promise<Product> {
    const product = await this.productRepo.findOne({
      where: { id, tenantId },
      relations: ['variants'],
    });
    if (!product) throw new NotFoundException('Produto não encontrado');
    return product;
  }

  async update(tenantId: string, id: string, dto: UpdateProductDto): Promise<Product> {
    const product = await this.findOne(tenantId, id);
    const { variants, ...fields } = dto;

    // Variações existentes fora do payload são removidas; as demais são
    // atualizadas in-place (por id) ou criadas. Nunca apaga-e-recria tudo:
    // variações já usadas em propostas/contratos têm FK que bloqueia o delete.
    const incomingIds = new Set(variants.filter((v) => v.id).map((v) => v.id));
    const toRemove = (product.variants ?? []).filter((v) => !incomingIds.has(v.id));

    if (toRemove.length) {
      const removeIds = toRemove.map((v) => v.id);
      const [usage] = await this.dataSource.query(
        `SELECT (
          SELECT COUNT(*) FROM proposal_items WHERE product_variant_id = ANY($1)
        ) + (
          SELECT COUNT(*) FROM contract_items WHERE product_variant_id = ANY($1)
        ) AS count`,
        [removeIds],
      );
      if (Number(usage.count) > 0) {
        throw new BadRequestException(
          'Não é possível remover uma variação já usada em propostas ou contratos existentes.',
        );
      }
      await this.variantRepo.delete(removeIds);
    }

    for (const v of variants) {
      const { id: variantId, ...variantFields } = v;
      if (variantId) {
        await this.variantRepo.update({ id: variantId, productId: id }, variantFields);
      } else {
        await this.variantRepo.save(this.variantRepo.create({ ...variantFields, productId: id, tenantId }));
      }
    }

    await this.productRepo.update({ id, tenantId }, fields);
    return this.findOne(tenantId, id);
  }

  async remove(tenantId: string, id: string): Promise<{ action: 'deleted' | 'inactive' }> {
    const product = await this.findOne(tenantId, id);

    const [usage] = await this.dataSource.query(
      `SELECT (
        SELECT COUNT(*) FROM proposal_items
        WHERE product_id = $1
           OR product_variant_id IN (SELECT id FROM product_variants WHERE product_id = $1)
      ) + (
        SELECT COUNT(*) FROM contract_items
        WHERE product_variant_id IN (SELECT id FROM product_variants WHERE product_id = $1)
      ) AS count`,
      [id],
    );
    const isUsed = Number(usage.count) > 0;

    if (isUsed) {
      product.status = 'inactive';
      await this.productRepo.save(product);
      return { action: 'inactive' };
    }

    // Hard delete — variants cascade via FK ON DELETE CASCADE
    await this.productRepo.delete({ id, tenantId });
    return { action: 'deleted' };
  }
}

// ============================================================
// CONTROLLER: ProductsController
// ============================================================
import {
  Controller, Get, Post, Put, Delete,
  Body, Param, Query, UseGuards, HttpCode, HttpStatus, Request,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';

@ApiTags('Produtos')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Post()
  @ApiOperation({ summary: 'Cadastrar produto com variações' })
  create(@Body() dto: CreateProductDto, @Request() req: any) {
    return this.productsService.create(req.user.tenantId, dto);
  }

  @Get()
  @ApiOperation({ summary: 'Listar produtos do tenant' })
  findAll(@Query('status') status: string, @Request() req: any) {
    return this.productsService.findAll(req.user.tenantId, status);
  }

  @Get(':id')
  @ApiOperation({ summary: 'Buscar produto por ID' })
  findOne(@Param('id') id: string, @Request() req: any) {
    return this.productsService.findOne(req.user.tenantId, id);
  }

  @Put(':id')
  @ApiOperation({ summary: 'Atualizar produto e variações' })
  update(@Param('id') id: string, @Body() dto: UpdateProductDto, @Request() req: any) {
    return this.productsService.update(req.user.tenantId, id, dto);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Excluir produto se não usado, ou inativar se usado em propostas' })
  remove(@Param('id') id: string, @Request() req: any) {
    return this.productsService.remove(req.user.tenantId, id);
  }
}

// ============================================================
// MODULE
// ============================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [TypeOrmModule.forFeature([Product, ProductVariant])],
  controllers: [ProductsController],
  providers: [ProductsService],
  exports: [ProductsService],
})
export class ProductsModule {}
