import { Injectable } from '@nestjs/common';
import { ClsService } from 'nestjs-cls';
import type {
  CreateOportunidadeDto,
  UpdateOportunidadeDto,
  FunilResponse,
  EtapaFunil,
  Oportunidade,
} from '@sar/api-interface';

type OportRow = {
  id: number;
  idCliente: number | null;
  nomeProspect: string | null;
  empresaProspect: string | null;
  titulo: string;
  etapa: string;
  valorEstimado: unknown;
  observacoes: string | null;
  idPedido: string | null;
  createdAt: Date;
  updatedAt: Date;
};

type PrismaCtx = {
  oportunidade: {
    findMany: (args: object) => Promise<OportRow[]>;
    create: (args: object) => Promise<OportRow>;
    update: (args: object) => Promise<OportRow>;
    delete: (args: object) => Promise<void>;
  };
  $queryRawUnsafe: <T>(sql: string, ...args: unknown[]) => Promise<T[]>;
};

type ClienteRow = { id_cliente: number; nome: string | null; razao: string | null };

@Injectable()
export class FunilService {
  constructor(private readonly cls: ClsService) {}

  private ctx(): { prisma: PrismaCtx; idEmpresa: number; codVendedor: number } {
    const prisma = this.cls.get('prisma') as PrismaCtx;
    if (!prisma) throw new Error('prisma não disponível no CLS');
    const idEmpresa = this.cls.get('idEmpresa') as number;
    const userId = this.cls.get('userId') as string | undefined;
    const codVendedor = userId ? parseInt(userId, 10) : 0;
    return { prisma, idEmpresa, codVendedor };
  }

  private toOp(r: OportRow, nomeMap: Map<number, string>): Oportunidade {
    return {
      id: r.id,
      idCliente: r.idCliente,
      nomeProspect: r.nomeProspect,
      empresaProspect: r.empresaProspect,
      titulo: r.titulo,
      etapa: r.etapa as EtapaFunil,
      valorEstimado: r.valorEstimado != null ? String(r.valorEstimado) : null,
      observacoes: r.observacoes,
      idPedido: r.idPedido,
      createdAt: r.createdAt.toISOString(),
      updatedAt: r.updatedAt.toISOString(),
      nomeCliente: r.idCliente ? (nomeMap.get(r.idCliente) ?? null) : null,
    };
  }

  async listar(): Promise<FunilResponse> {
    const { prisma, idEmpresa, codVendedor } = this.ctx();

    const rows = await prisma.oportunidade.findMany({
      where: { idEmpresa, codVendedor },
      orderBy: { updatedAt: 'desc' },
    });

    const idClientes = [
      ...new Set(rows.filter((r) => r.idCliente).map((r) => r.idCliente as number)),
    ];
    const nomeMap = new Map<number, string>();
    if (idClientes.length > 0) {
      const ids = idClientes.join(',');
      const clientes = await prisma.$queryRawUnsafe<ClienteRow>(
        `SELECT id_cliente, TRIM(nome) AS nome, TRIM(razao) AS razao FROM vw_clientes WHERE id_cliente IN (${ids}) AND id_empresa = ${idEmpresa}`,
      );
      for (const c of clientes) {
        nomeMap.set(c.id_cliente, c.razao ?? c.nome ?? `Cód. ${c.id_cliente}`);
      }
    }

    const byEtapa = (etapa: EtapaFunil) =>
      rows.filter((r) => r.etapa === etapa).map((r) => this.toOp(r, nomeMap));

    return {
      lead: byEtapa('lead'),
      proposta: byEtapa('proposta'),
      negociacao: byEtapa('negociacao'),
      ganho: byEtapa('ganho'),
      perdido: byEtapa('perdido'),
    };
  }

  async criar(dto: CreateOportunidadeDto): Promise<Oportunidade> {
    const { prisma, idEmpresa, codVendedor } = this.ctx();
    const row = await prisma.oportunidade.create({
      data: {
        idEmpresa,
        codVendedor,
        idCliente: dto.idCliente ?? null,
        nomeProspect: dto.nomeProspect ?? null,
        empresaProspect: dto.empresaProspect ?? null,
        titulo: dto.titulo,
        etapa: dto.etapa ?? 'lead',
        valorEstimado: dto.valorEstimado ?? null,
        observacoes: dto.observacoes ?? null,
      },
    });
    return this.toOp(row, new Map());
  }

  async atualizar(id: number, dto: UpdateOportunidadeDto): Promise<Oportunidade> {
    const { prisma, idEmpresa, codVendedor } = this.ctx();
    const row = await prisma.oportunidade.update({
      where: { id, idEmpresa, codVendedor },
      data: {
        ...(dto.idCliente !== undefined && { idCliente: dto.idCliente }),
        ...(dto.nomeProspect !== undefined && { nomeProspect: dto.nomeProspect }),
        ...(dto.empresaProspect !== undefined && { empresaProspect: dto.empresaProspect }),
        ...(dto.titulo !== undefined && { titulo: dto.titulo }),
        ...(dto.etapa !== undefined && { etapa: dto.etapa }),
        ...(dto.valorEstimado !== undefined && { valorEstimado: dto.valorEstimado }),
        ...(dto.observacoes !== undefined && { observacoes: dto.observacoes }),
        ...(dto.idPedido !== undefined && { idPedido: dto.idPedido }),
      },
    });
    return this.toOp(row, new Map());
  }

  async deletar(id: number): Promise<void> {
    const { prisma, idEmpresa, codVendedor } = this.ctx();
    await prisma.oportunidade.delete({ where: { id, idEmpresa, codVendedor } });
  }
}
