import {
  Injectable,
  UnauthorizedException,
  ConflictException,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';
import * as crypto from 'crypto';
import { PrismaService } from '../prisma/prisma.service';
import { RegisterDto, LoginDto } from './dto';
import { MailService } from '../mail/mail.service';
import { ResetPasswordDto } from './dto/reset-password.dto';

@Injectable()
export class AuthService {
  constructor(
    private prisma: PrismaService,
    private jwtService: JwtService,
    private mailService: MailService,
  ) {}

  async register(registerDto: RegisterDto) {
    const { email, username, password, role } = registerDto;

    // Prüfen ob User bereits existiert
    const existingUser = await this.prisma.user.findFirst({
      where: {
        OR: [{ email }, { username }],
      },
    });

    if (existingUser) {
      if (existingUser.email === email) {
        throw new ConflictException('Email bereits registriert');
      }
      if (existingUser.username === username) {
        throw new ConflictException('Username bereits vergeben');
      }
    }

    // Password hashen
    const hashedPassword = await bcrypt.hash(password, 10);

    // User erstellen
    const user = await this.prisma.user.create({
      data: {
        email,
        username,
        password: hashedPassword,
        role: role || 'Student',
      },
    });

    // Willkommens-Email senden
    try {
      await this.mailService.sendUserConfirmation(user);
    } catch (error) {
      // Log error but don't fail registration
      console.error('Email sending failed:', error);
    }

    // JWT Token generieren
    const token = await this.generateToken(user.id, user.username, user.role, user.email);

    return {
      message: 'Registrierung erfolgreich',
      user: {
        id: user.id,
        email: user.email,
        username: user.username,
        role: user.role,
      },
      token,
    };
  }

  async login(loginDto: LoginDto) {
    const { username, password } = loginDto;

    // User finden
    const user = await this.prisma.user.findUnique({
      where: { username },
    });

    if (!user) {
      throw new UnauthorizedException('Ungültige Anmeldedaten');
    }

    // Password vergleichen
    const isPasswordValid = await bcrypt.compare(password, user.password);

    if (!isPasswordValid) {
      throw new UnauthorizedException('Ungültige Anmeldedaten');
    }

    // JWT Token generieren
    const token = await this.generateToken(user.id, user.username, user.role, user.email);

    return {
      message: 'Login erfolgreich',
      user: {
        id: user.id,
        email: user.email,
        username: user.username,
        role: user.role,
      },
      token,
    };
  }

  async forgotPassword(email: string) {
    const user = await this.prisma.user.findUnique({
      where: { email },
    });

    if (user) {
      const token = crypto.randomBytes(32).toString('hex');
      const expires = new Date();
      expires.setHours(expires.getHours() + 1); // 1 Stunde gültig

      await this.prisma.user.update({
        where: { id: user.id },
        data: {
          passwordResetToken: token,
          passwordResetExpires: expires,
        },
      });

      try {
        await this.mailService.sendPasswordReset(user, token);
      } catch (error) {
        console.error('Email sending failed:', error);
      }
    }

    // Immer die gleiche Nachricht zurückgeben, egal ob User existiert oder nicht
    return { message: 'Falls ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.' };
  }

  async resetPassword(resetPasswordDto: ResetPasswordDto) {
    const { token, newPassword } = resetPasswordDto;

    const user = await this.prisma.user.findFirst({
      where: {
        passwordResetToken: token,
        passwordResetExpires: {
          gt: new Date(),
        },
      },
    });

    if (!user) {
      throw new BadRequestException('Ungültiger oder abgelaufener Token');
    }

    const hashedPassword = await bcrypt.hash(newPassword, 10);

    await this.prisma.user.update({
      where: { id: user.id },
      data: {
        password: hashedPassword,
        passwordResetToken: null,
        passwordResetExpires: null,
      },
    });

    return { message: 'Passwort erfolgreich geändert' };
  }

  async validateUser(userId: number) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
    });

    if (!user) {
      throw new UnauthorizedException('User nicht gefunden');
    }

    return {
      id: user.id,
      email: user.email,
      username: user.username,
      role: user.role,
    };
  }

  private async generateToken(userId: number, username: string, role: string, email?: string) {
    const payload = { sub: userId, username, role, email };
    return this.jwtService.sign(payload);
  }
}
