import { Injectable, NotFoundException } from '@nestjs/common';
import { AccountStatus, UserRole, WalletType } from '@prisma/client';
import bcrypt from 'bcrypt';
import { PrismaService } from '../database/prisma.service';
import { UpsertCustomerDto } from './dto/upsert-customer.dto';

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

  list() {
    return this.prisma.customer.findMany({
      orderBy: { createdAt: 'desc' },
      include: {
        user: true,
        wallet: true,
        _count: { select: { orders: true } },
      },
    });
  }

  async create(data: UpsertCustomerDto) {
    const passwordHash = await bcrypt.hash(data.password, 12);

    return this.prisma.$transaction(async (tx) => {
      const user = await tx.user.create({
        data: {
          email: data.email.trim().toLowerCase(),
          phone: data.phone.trim(),
          passwordHash,
          role: UserRole.CUSTOMER,
          status: data.status ?? AccountStatus.ACTIVE,
          emailVerifiedAt: new Date(),
          phoneVerifiedAt: new Date(),
        },
      });

      const customer = await tx.customer.create({
        data: {
          userId: user.id,
          fullName: data.fullName,
        },
        include: { user: true, wallet: true, _count: { select: { orders: true } } },
      });

      await tx.wallet.create({
        data: { userId: user.id, customerId: customer.id, type: WalletType.CUSTOMER, balance: 0 },
      });

      return customer;
    });
  }

  async update(id: string, data: Partial<UpsertCustomerDto>) {
    const current = await this.prisma.customer.findUnique({ where: { id } });
    if (!current) throw new NotFoundException('Cliente nao encontrado.');

    const userUpdate: Record<string, unknown> = {};
    if (data.email) userUpdate.email = data.email.trim().toLowerCase();
    if (data.phone) userUpdate.phone = data.phone.trim();
    if (data.password) userUpdate.passwordHash = await bcrypt.hash(data.password, 12);
    if (data.status) userUpdate.status = data.status;

    return this.prisma.$transaction(async (tx) => {
      if (Object.keys(userUpdate).length) {
        await tx.user.update({ where: { id: current.userId }, data: userUpdate });
      }

      return tx.customer.update({
        where: { id },
        data: { fullName: data.fullName },
        include: { user: true, wallet: true, _count: { select: { orders: true } } },
      });
    });
  }

  async archive(id: string) {
    const current = await this.prisma.customer.findUnique({ where: { id } });
    if (!current) throw new NotFoundException('Cliente nao encontrado.');

    await this.prisma.user.update({
      where: { id: current.userId },
      data: { status: AccountStatus.SUSPENDED },
    });

    return { id, archived: true };
  }

  async getOwnProfile(userId: string) {
    const customer = await this.prisma.customer.findUnique({
      where: { userId },
      include: { user: true },
    });
    if (!customer) {
      throw new NotFoundException('Cliente autenticado nao encontrado.');
    }

    return {
      id: customer.id,
      fullName: customer.fullName,
      email: customer.user.email,
      phone: customer.user.phone,
    };
  }

  async updateOwnProfile(userId: string, data: { fullName?: string; email?: string; phone?: string }) {
    const customer = await this.prisma.customer.findUnique({ where: { userId } });
    if (!customer) {
      throw new NotFoundException('Cliente autenticado nao encontrado.');
    }

    const userUpdate: Record<string, unknown> = {};
    if (data.email) userUpdate.email = data.email.trim().toLowerCase();
    if (data.phone) userUpdate.phone = data.phone.trim();

    return this.prisma.$transaction(async (tx) => {
      if (Object.keys(userUpdate).length) {
        await tx.user.update({ where: { id: userId }, data: userUpdate });
      }

      const updated = await tx.customer.update({
        where: { id: customer.id },
        data: { fullName: data.fullName ?? customer.fullName },
        include: { user: true },
      });

      return {
        id: updated.id,
        fullName: updated.fullName,
        email: updated.user.email,
        phone: updated.user.phone,
      };
    });
  }

  async getOwnAddress(userId: string) {
    const address = await this.prisma.address.findFirst({
      where: { userId },
      orderBy: [{ isDefault: 'desc' }, { createdAt: 'desc' }],
    });
    if (!address) {
      throw new NotFoundException('Endereco do cliente nao encontrado.');
    }
    return address;
  }

  async updateOwnAddress(
    userId: string,
    data: { label?: string; street?: string; number?: string; complement?: string; neighborhood?: string; city?: string; state?: string; zipCode?: string },
  ) {
    const current = await this.prisma.address.findFirst({
      where: { userId },
      orderBy: [{ isDefault: 'desc' }, { createdAt: 'desc' }],
    });

    if (!current) {
      return this.prisma.address.create({
        data: {
          userId,
          label: data.label ?? 'Endereco principal',
          street: data.street ?? '',
          number: data.number ?? '',
          complement: data.complement ?? '',
          neighborhood: data.neighborhood ?? '',
          city: data.city ?? '',
          state: data.state ?? '',
          zipCode: data.zipCode ?? '',
          isDefault: true,
        },
      });
    }

    return this.prisma.address.update({
      where: { id: current.id },
      data: {
        label: data.label ?? current.label,
        street: data.street ?? current.street,
        number: data.number ?? current.number,
        complement: data.complement ?? current.complement,
        neighborhood: data.neighborhood ?? current.neighborhood,
        city: data.city ?? current.city,
        state: data.state ?? current.state,
        zipCode: data.zipCode ?? current.zipCode,
      },
    });
  }
}
