import {
  Injectable,
  ConflictException,
  NotFoundException,
  UnauthorizedException,
  Logger,
} from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { ConfigService } from '@nestjs/config';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  private readonly logger = new Logger(UsersService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly config: ConfigService,
  ) {}

  /**
   * Neuen User anlegen (mit lokalem Auth-Provider).
   */
  async create(dto: CreateUserDto) {
    // Email-Duplikat pruefen
    const existing = await this.prisma.user.findUnique({
      where: { email: dto.email.toLowerCase() },
    });
    if (existing) {
      throw new ConflictException('E-Mail-Adresse bereits vergeben');
    }

    // Passwort hashen (Bcrypt Cost 12)
    const bcryptCost = this.config.get<number>('BCRYPT_COST', 12);
    const passwordHash = await bcrypt.hash(dto.password, bcryptCost);

    // User + AuthProvider in einer Transaktion anlegen
    const user = await this.prisma.user.create({
      data: {
        email: dto.email.toLowerCase(),
        firstName: dto.firstName,
        lastName: dto.lastName,
        role: dto.role ?? 'USER',
        isActive: true,
        authProvider: {
          create: {
            provider: 'LOCAL',
            passwordHash,
          },
        },
      },
      include: {
        authProvider: {
          select: { provider: true, createdAt: true },
        },
      },
    });

    this.logger.log(`User erstellt: ${user.email} (${user.role})`);

    // Passwort-Hash nicht zurückgeben
    return {
      id: user.id,
      email: user.email,
      firstName: user.firstName,
      lastName: user.lastName,
      role: user.role,
      isActive: user.isActive,
      createdAt: user.createdAt,
    };
  }

  /**
   * User nach ID finden.
   */
  async findById(id: string) {
    const user = await this.prisma.user.findUnique({
      where: { id },
      include: {
        tenantMemberships: {
          include: { tenant: { select: { id: true, name: true, slug: true } } },
          where: { isActive: true },
        },
      },
    });

    if (!user) {
      throw new NotFoundException('Benutzer nicht gefunden');
    }

    return {
      id: user.id,
      email: user.email,
      firstName: user.firstName,
      lastName: user.lastName,
      avatar: user.avatar,
      phone: user.phone,
      mobile: user.mobile,
      street: user.street,
      postalCode: user.postalCode,
      city: user.city,
      jobTitle: user.jobTitle,
      department: user.department,
      companyName: user.companyName,
      officeLocation: user.officeLocation,
      role: user.role,
      isActive: user.isActive,
      twoFactorEnabled: user.twoFactorEnabled,
      tenants: user.tenantMemberships.map((m) => ({
        id: m.tenant.id,
        name: m.tenant.name,
        slug: m.tenant.slug,
        role: m.tenantRole,
      })),
      lastLogin: user.lastLogin,
      createdAt: user.createdAt,
    };
  }

  /**
   * User aktualisieren.
   */
  async update(id: string, dto: UpdateUserDto) {
    const user = await this.prisma.user.findUnique({ where: { id } });
    if (!user) {
      throw new NotFoundException('Benutzer nicht gefunden');
    }

    const updated = await this.prisma.user.update({
      where: { id },
      data: {
        firstName: dto.firstName,
        lastName: dto.lastName,
        isActive: dto.isActive,
        ...(dto.avatar !== undefined && { avatar: dto.avatar }),
        ...(dto.phone !== undefined && { phone: dto.phone }),
        ...(dto.mobile !== undefined && { mobile: dto.mobile }),
        ...(dto.street !== undefined && { street: dto.street }),
        ...(dto.postalCode !== undefined && { postalCode: dto.postalCode }),
        ...(dto.city !== undefined && { city: dto.city }),
        ...(dto.jobTitle !== undefined && { jobTitle: dto.jobTitle }),
        ...(dto.department !== undefined && { department: dto.department }),
        ...(dto.companyName !== undefined && { companyName: dto.companyName }),
        ...(dto.officeLocation !== undefined && { officeLocation: dto.officeLocation }),
      },
    });

    return {
      id: updated.id,
      email: updated.email,
      firstName: updated.firstName,
      lastName: updated.lastName,
      avatar: updated.avatar,
      phone: updated.phone,
      mobile: updated.mobile,
      street: updated.street,
      postalCode: updated.postalCode,
      city: updated.city,
      jobTitle: updated.jobTitle,
      department: updated.department,
      companyName: updated.companyName,
      officeLocation: updated.officeLocation,
      role: updated.role,
      isActive: updated.isActive,
    };
  }

  /**
   * Eigenes Profil aktualisieren (nur firstName, lastName).
   */
  async updateProfile(userId: string, dto: UpdateUserDto) {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('Benutzer nicht gefunden');
    }

    const updated = await this.prisma.user.update({
      where: { id: userId },
      data: {
        ...(dto.firstName !== undefined && { firstName: dto.firstName }),
        ...(dto.lastName !== undefined && { lastName: dto.lastName }),
        ...(dto.avatar !== undefined && { avatar: dto.avatar }),
        ...(dto.phone !== undefined && { phone: dto.phone }),
        ...(dto.mobile !== undefined && { mobile: dto.mobile }),
        ...(dto.street !== undefined && { street: dto.street }),
        ...(dto.postalCode !== undefined && { postalCode: dto.postalCode }),
        ...(dto.city !== undefined && { city: dto.city }),
        ...(dto.jobTitle !== undefined && { jobTitle: dto.jobTitle }),
        ...(dto.department !== undefined && { department: dto.department }),
        ...(dto.companyName !== undefined && { companyName: dto.companyName }),
        ...(dto.officeLocation !== undefined && { officeLocation: dto.officeLocation }),
      },
    });

    return {
      id: updated.id,
      email: updated.email,
      firstName: updated.firstName,
      lastName: updated.lastName,
      avatar: updated.avatar,
      phone: updated.phone,
      mobile: updated.mobile,
      street: updated.street,
      postalCode: updated.postalCode,
      city: updated.city,
      jobTitle: updated.jobTitle,
      department: updated.department,
      companyName: updated.companyName,
      officeLocation: updated.officeLocation,
      role: updated.role,
      isActive: updated.isActive,
      twoFactorEnabled: updated.twoFactorEnabled,
    };
  }

  /**
   * Eigenes Passwort ändern (mit Verifikation des aktuellen Passworts).
   */
  async changePassword(
    userId: string,
    currentPassword: string,
    newPassword: string,
  ): Promise<void> {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      include: { authProvider: true },
    });

    if (!user) {
      throw new NotFoundException('Benutzer nicht gefunden');
    }

    const localAuth = user.authProvider.find((ap) => ap.provider === 'LOCAL');
    if (!localAuth?.passwordHash) {
      throw new NotFoundException('Kein lokaler Auth-Provider gefunden');
    }

    // Aktuelles Passwort verifizieren
    const isCurrentValid = await bcrypt.compare(
      currentPassword,
      localAuth.passwordHash,
    );
    if (!isCurrentValid) {
      throw new UnauthorizedException('Aktuelles Passwort ist falsch');
    }

    // Neues Passwort hashen (Bcrypt Cost 12)
    const bcryptCost = this.config.get<number>('BCRYPT_COST', 12);
    const newHash = await bcrypt.hash(newPassword, bcryptCost);

    // Passwort aktualisieren
    await this.prisma.authProvider.update({
      where: { id: localAuth.id },
      data: { passwordHash: newHash },
    });

    this.logger.log(`Passwort geändert für User ${user.email}`);
  }

  /**
   * User löschen (inkl. Auth-Provider, Memberships, Profil via Cascade).
   */
  async delete(id: string) {
    const user = await this.prisma.user.findUnique({ where: { id } });
    if (!user) {
      throw new NotFoundException('Benutzer nicht gefunden');
    }

    await this.prisma.user.delete({ where: { id } });
    this.logger.log(`User gelöscht: ${user.email}`);

    return { message: 'Benutzer wurde gelöscht' };
  }

  /**
   * Alle User auflisten (für Admin).
   */
  async findAll(page = 1, limit = 20) {
    const skip = (page - 1) * limit;

    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        skip,
        take: limit,
        orderBy: { createdAt: 'desc' },
        select: {
          id: true,
          email: true,
          firstName: true,
          lastName: true,
          role: true,
          isActive: true,
          lastLogin: true,
          createdAt: true,
        },
      }),
      this.prisma.user.count(),
    ]);

    return {
      data: users,
      meta: {
        total,
        page,
        limit,
        totalPages: Math.ceil(total / limit),
      },
    };
  }
}
