import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import { PrismaService } from 'src/prisma/prisma.service';
import type { StoreJwtPayload } from './store-jwt.strategy';
import type { StoreLoginDto, StoreRegisterDto, StoreUpdateProfileDto, StoreSyncCartDto } from './store-auth.dto';

@Injectable()
export class StoreAuthService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly jwt: JwtService,
  ) {}

  private async resolveEmpresa(subdominio: string) {
    const config = await this.prisma.ecommerce_config.findUnique({
      where: { subdominio },
      select: { empresa_id: true },
    });
    if (!config) throw new BadRequestException('Tienda no encontrada');
    return config.empresa_id;
  }

  private buildToken(clienteId: string, empresaId: string) {
    const payload: StoreJwtPayload = { sub: clienteId, empresa_id: empresaId, scope: 'store_customer' };
    return this.jwt.sign(payload, { expiresIn: '30d' });
  }

  async register(dto: StoreRegisterDto) {
    const empresa_id = await this.resolveEmpresa(dto.subdominio);

    const existing = await this.prisma.ecommerce_clientes.findUnique({
      where: { empresa_id_email: { empresa_id, email: dto.email.toLowerCase() } },
    });
    if (existing) throw new BadRequestException('Ya existe una cuenta con ese email en esta tienda');

    const password_hash = await bcrypt.hash(dto.password, 10);
    const cliente = await this.prisma.ecommerce_clientes.create({
      data: {
        empresa_id,
        email: dto.email.toLowerCase(),
        password_hash,
        nombre: dto.nombre,
        telefono: dto.telefono,
        documento: dto.documento,
      },
      select: { id: true, empresa_id: true, email: true, nombre: true, telefono: true, documento: true },
    });

    return { access_token: this.buildToken(cliente.id, empresa_id), cliente };
  }

  async login(dto: StoreLoginDto) {
    const empresa_id = await this.resolveEmpresa(dto.subdominio);

    const cliente = await this.prisma.ecommerce_clientes.findUnique({
      where: { empresa_id_email: { empresa_id, email: dto.email.toLowerCase() } },
      select: { id: true, empresa_id: true, email: true, nombre: true, telefono: true, documento: true, activo: true, password_hash: true },
    });

    if (!cliente || !cliente.activo) throw new UnauthorizedException('Credenciales inválidas');
    const ok = await bcrypt.compare(dto.password, cliente.password_hash);
    if (!ok) throw new UnauthorizedException('Credenciales inválidas');

    const { password_hash: _, ...safeCliente } = cliente;
    return { access_token: this.buildToken(cliente.id, empresa_id), cliente: safeCliente };
  }

  async me(clienteId: string) {
    return this.prisma.ecommerce_clientes.findUnique({
      where: { id: clienteId },
      select: { id: true, empresa_id: true, email: true, nombre: true, telefono: true, documento: true, created_at: true },
    });
  }

  async updateProfile(clienteId: string, dto: StoreUpdateProfileDto) {
    return this.prisma.ecommerce_clientes.update({
      where: { id: clienteId },
      data: {
        ...(dto.nombre && { nombre: dto.nombre }),
        ...(dto.telefono !== undefined && { telefono: dto.telefono }),
        ...(dto.documento !== undefined && { documento: dto.documento }),
        updated_at: new Date(),
      },
      select: { id: true, email: true, nombre: true, telefono: true, documento: true },
    });
  }

  async syncCart(clienteId: string, dto: StoreSyncCartDto) {
    const empresa_id = await this.resolveEmpresa(dto.subdominio);
    await this.prisma.ecommerce_cart.upsert({
      where: { cliente_id: clienteId },
      create: { empresa_id, cliente_id: clienteId, items: dto.items },
      update: { items: dto.items, updated_at: new Date() },
    });
    return { ok: true };
  }

  async getCart(clienteId: string) {
    const cart = await this.prisma.ecommerce_cart.findUnique({
      where: { cliente_id: clienteId },
      select: { items: true, updated_at: true },
    });
    return { items: (cart?.items as object[]) ?? [] };
  }

  async getOrders(clienteId: string) {
    const cliente = await this.prisma.ecommerce_clientes.findUnique({
      where: { id: clienteId },
      select: { empresa_id: true, email: true },
    });
    if (!cliente) return { orders: [] };

    type OrderRow = {
      id: string; codigo: string; token: string; estado: string;
      metodo_pago: string; customer_snapshot: unknown; delivery_snapshot: unknown;
      items_snapshot: unknown; totals_snapshot: unknown; created_at: Date;
      comentarios_cliente: string | null;
    };

    const orders = await this.prisma.$queryRawUnsafe<OrderRow[]>(
      `SELECT id, codigo, token, estado, metodo_pago,
              customer_snapshot, delivery_snapshot, items_snapshot, totals_snapshot,
              comentarios_cliente, created_at
       FROM ecommerce_checkout_session
       WHERE empresa_id = $1::uuid
         AND customer_snapshot->>'email' = $2
       ORDER BY created_at DESC
       LIMIT 50`,
      cliente.empresa_id,
      cliente.email,
    );

    if (!orders.length) return { orders: [] };

    // Historial de estados por pedido. Se escribía en cada cambio del ERP pero nunca
    // llegaba al cliente. Se expone un subconjunto seguro: `metadata` lleva notas
    // internas del operador y `created_by` el usuario del ERP.
    const eventos = await this.prisma.$queryRawUnsafe<
      { checkout_session_id: string; tipo: string; estado_anterior: string | null; estado_nuevo: string | null; created_at: Date }[]
    >(
      `SELECT checkout_session_id, tipo, estado_anterior, estado_nuevo, created_at
       FROM ecommerce_pedido_evento
       WHERE empresa_id = $1::uuid AND checkout_session_id = ANY($2::uuid[])
       ORDER BY created_at ASC`,
      cliente.empresa_id,
      orders.map((o) => o.id),
    );

    const porPedido = new Map<string, { tipo: string; estadoAnterior: string | null; estadoNuevo: string | null; fecha: Date }[]>();
    for (const e of eventos) {
      const lista = porPedido.get(e.checkout_session_id) ?? [];
      lista.push({
        tipo: e.tipo,
        estadoAnterior: e.estado_anterior,
        estadoNuevo: e.estado_nuevo,
        fecha: e.created_at,
      });
      porPedido.set(e.checkout_session_id, lista);
    }

    return {
      orders: orders.map((o) => ({
        ...o,
        eventos: [
          // El alta no genera evento propio, pero es el primer hito real del pedido.
          { tipo: 'creacion', estadoAnterior: null, estadoNuevo: 'pendiente_pago', fecha: o.created_at },
          ...(porPedido.get(o.id) ?? []),
        ],
      })),
    };
  }

  async submitReview(
    clienteId: string,
    productoId: string,
    dto: { rating: number; comment?: string },
  ) {
    if (!Number.isInteger(dto.rating) || dto.rating < 1 || dto.rating > 5) {
      throw new BadRequestException('El rating debe ser un entero entre 1 y 5');
    }

    const cliente = await this.prisma.ecommerce_clientes.findUnique({
      where: { id: clienteId },
      select: { empresa_id: true },
    });
    if (!cliente) throw new BadRequestException('Cliente no encontrado');

    // Verificar que el producto pertenece a la misma empresa
    const productoExists = await this.prisma.$queryRawUnsafe<{ id: string }[]>(
      `SELECT id FROM productos WHERE id = $1::uuid AND empresa_id = $2::uuid AND (deleted IS NULL OR deleted = false) LIMIT 1`,
      productoId,
      cliente.empresa_id,
    );
    if (!productoExists.length) throw new BadRequestException('Producto no encontrado');

    // Upsert: si ya existe una reseña del cliente para este producto, la actualiza
    const existing = await this.prisma.ecommerce_resena.findFirst({
      where: { empresa_id: cliente.empresa_id, producto_id: productoId, cliente_id: clienteId },
      select: { id: true },
    });

    if (existing) {
      await this.prisma.ecommerce_resena.update({
        where: { id: existing.id },
        data: { rating: dto.rating, comment: dto.comment ?? null, updated_at: new Date() },
      });
    } else {
      await this.prisma.ecommerce_resena.create({
        data: {
          empresa_id: cliente.empresa_id,
          producto_id: productoId,
          cliente_id: clienteId,
          rating: dto.rating,
          comment: dto.comment ?? null,
        },
      });
    }

    return { ok: true };
  }
}
