import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import {
  Entity, PrimaryGeneratedColumn, Column,
  CreateDateColumn, UpdateDateColumn,
} from 'typeorm';
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import {
  Controller, Get, Post, Put, Delete,
  Body, Param, Query, UseGuards, Request,
  HttpCode, HttpStatus,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import {
  IsString, IsOptional, IsEmail, IsIn, MinLength,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

// ============================================================
// ENTITY
// ============================================================
@Entity('clients')
export class Client {
  @PrimaryGeneratedColumn('uuid') id: string;
  @Column({ name: 'tenant_id' }) tenantId: string;
  @Column({ length: 2, default: 'PJ' }) type: string;
  @Column({ length: 150 }) name: string;
  @Column({ nullable: true }) document: string;
  @Column({ length: 80, nullable: true }) email: string;
  @Column({ nullable: true }) phone: string;
  @Column({ nullable: true }) address: string;
  @Column({ name: 'address_number', nullable: true }) addressNumber: string;
  @Column({ name: 'address_complement', nullable: true }) addressComplement: string;
  @Column({ nullable: true }) neighborhood: string;
  @Column({ name: 'ibge_code', nullable: true }) ibgeCode: string;
  @Column({ nullable: true }) uf: string;
  @Column({ name: 'zip_code', nullable: true }) zipCode: string;
  @Column({ name: 'municipal_inscription', nullable: true }) municipalInscription: string;
  // Contact section
  @Column({ name: 'phone_commercial', nullable: true }) phoneCommercial: string;
  @Column({ name: 'phone_financial', nullable: true }) phoneFinancial: string;
  @Column({ name: 'email_commercial', length: 80, nullable: true }) emailCommercial: string;
  @Column({ name: 'email_financial', length: 80, nullable: true }) emailFinancial: string;
  @Column({ name: 'email_nf', length: 80, nullable: true }) emailNf: string;
  @Column({ type: 'text', nullable: true }) notes: string;
  @Column({ default: 'active' }) status: string;
  @CreateDateColumn({ name: 'created_at' }) createdAt: Date;
  @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date;
}

// ============================================================
// DTOs
// ============================================================
export class CreateClientDto {
  @ApiProperty({ enum: ['PF', 'PJ'], default: 'PJ' })
  @IsIn(['PF', 'PJ']) type: string;

  @ApiProperty({ example: 'Empresa ABC Ltda' })
  @IsString() @MinLength(2) name: string;

  @ApiPropertyOptional({ example: '12345678000195' })
  @IsOptional() @IsString() document?: string;

  @ApiPropertyOptional() @IsOptional() @IsEmail() email?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() phone?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() address?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() addressNumber?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() addressComplement?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() neighborhood?: string;
  @ApiPropertyOptional({ description: 'Código IBGE' }) @IsOptional() @IsString() ibgeCode?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() uf?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() zipCode?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() municipalInscription?: string;
  @ApiPropertyOptional({ description: 'Telefone comercial' }) @IsOptional() @IsString() phoneCommercial?: string;
  @ApiPropertyOptional({ description: 'Telefone financeiro' }) @IsOptional() @IsString() phoneFinancial?: string;
  @ApiPropertyOptional({ description: 'Email comercial' }) @IsOptional() @IsEmail() emailCommercial?: string;
  @ApiPropertyOptional({ description: 'Email financeiro' }) @IsOptional() @IsEmail() emailFinancial?: string;
  @ApiPropertyOptional({ description: 'Email para envio de NF' }) @IsOptional() @IsEmail() emailNf?: string;
  @ApiPropertyOptional() @IsOptional() @IsString() notes?: string;
}

export class UpdateClientDto extends CreateClientDto {
  @ApiPropertyOptional({ enum: ['active', 'inactive'] })
  @IsOptional() @IsIn(['active', 'inactive']) status?: string;
}

// ============================================================
// SERVICE
// ============================================================
@Injectable()
export class ClientsService {
  constructor(
    @InjectRepository(Client)
    private readonly clientRepo: Repository<Client>,
  ) {}

  async create(tenantId: string, dto: CreateClientDto): Promise<Client> {
    if (dto.document) {
      const exists = await this.clientRepo.findOne({
        where: { tenantId, document: dto.document, status: 'active' },
      });
      if (exists) {
        throw new ConflictException(
          `${dto.type === 'PJ' ? 'CNPJ' : 'CPF'} ${dto.document} já está cadastrado`,
        );
      }
    }
    return this.clientRepo.save(this.clientRepo.create({ ...dto, tenantId }));
  }

  async findAll(tenantId: string, search?: string): Promise<Client[]> {
    const qb = this.clientRepo.createQueryBuilder('c')
      .where('c.tenantId = :tenantId', { tenantId })
      .andWhere('c.status = :s', { s: 'active' })
      .orderBy('c.name', 'ASC');

    if (search) {
      qb.andWhere('(c.name ILIKE :q OR c.document LIKE :q OR c.email ILIKE :q)', {
        q: `%${search}%`,
      });
    }

    return qb.getMany();
  }

  async findOne(tenantId: string, id: string): Promise<Client> {
    const client = await this.clientRepo.findOne({ where: { id, tenantId } });
    if (!client) throw new NotFoundException('Cliente não encontrado');
    return client;
  }

  async update(tenantId: string, id: string, dto: UpdateClientDto): Promise<Client> {
    const client = await this.findOne(tenantId, id);
    Object.assign(client, dto);
    return this.clientRepo.save(client);
  }

  async remove(tenantId: string, id: string): Promise<void> {
    const client = await this.findOne(tenantId, id);
    client.status = 'inactive';
    await this.clientRepo.save(client);
  }
}

// ============================================================
// CONTROLLER
// ============================================================
@ApiTags('Clientes')
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'))
@Controller('clients')
export class ClientsController {
  constructor(private readonly clientsService: ClientsService) {}

  @Post()
  @ApiOperation({ summary: 'Cadastrar cliente' })
  create(@Body() dto: CreateClientDto, @Request() req: any) {
    return this.clientsService.create(req.user.tenantId, dto);
  }

  @Get()
  @ApiOperation({ summary: 'Listar clientes' })
  findAll(@Query('search') search: string, @Request() req: any) {
    return this.clientsService.findAll(req.user.tenantId, search);
  }

  @Get(':id')
  @ApiOperation({ summary: 'Detalhar cliente' })
  findOne(@Param('id') id: string, @Request() req: any) {
    return this.clientsService.findOne(req.user.tenantId, id);
  }

  @Put(':id')
  @ApiOperation({ summary: 'Atualizar cliente' })
  update(@Param('id') id: string, @Body() dto: UpdateClientDto, @Request() req: any) {
    return this.clientsService.update(req.user.tenantId, id, dto);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Inativar cliente' })
  remove(@Param('id') id: string, @Request() req: any) {
    return this.clientsService.remove(req.user.tenantId, id);
  }
}

// ============================================================
// MODULE
// ============================================================
@Module({
  imports: [TypeOrmModule.forFeature([Client])],
  controllers: [ClientsController],
  providers: [ClientsService],
  exports: [ClientsService],
})
export class ClientsModule {}
