import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import UserRepository from '../../infrastructure/repositories/UserRepository';
import ProfileRepository from '../../infrastructure/repositories/ProfileRepository';
import PasswordResetRepository from '../../infrastructure/repositories/PasswordResetRepository';
import { generateNIM } from '../../utils/helpers';

const prisma = new PrismaClient();
const userRepo = new UserRepository(prisma);
const profileRepo = new ProfileRepository(prisma);
const resetRepo = new PasswordResetRepository(prisma);

export class AuthService {
  constructor() {
    this.secret = process.env.JWT_SECRET;
    this.refreshSecret = process.env.REFRESH_TOKEN_SECRET;
    this.expiresIn = process.env.JWT_EXPIRES_IN || '7d';
    this.refreshExpiresIn = process.env.REFRESH_TOKEN_EXPIRES_IN || '30d';
  }

  async register(data) {
    const exists = await userRepo.findByEmail(data.email);
    if (exists) throw new Error('Email sudah terdaftar');
    const password = await bcrypt.hash(data.password, 12);
    const nim = await generateNIM(prisma);
    const user = await userRepo.create({
      email: data.email,
      password,
      role: 'student',
      nim,
      profile: { create: { fullName: data.fullName, statusPendaftaran: 'pending', ...data } },
    });
    const tokens = this.generateTokens(user);
    return { user, tokens };
  }

  async login(email, password) {
    const user = await userRepo.findByEmail(email);
    if (!user || !await bcrypt.compare(password, user.password)) throw new Error('Email atau password salah');
    if (!user.isActive) throw new Error('Akun dinonaktifkan');
    const tokens = this.generateTokens(user);
    return { user, tokens };
  }

  async refresh(token) {
    const decoded = jwt.verify(token, this.refreshSecret);
    const user = await userRepo.findById(decoded.id);
    if (!user) throw new Error('Invalid refresh token');
    const tokens = this.generateTokens(user);
    return tokens;
  }

  async forgotPassword(email) {
    const user = await userRepo.findByEmail(email);
    if (!user) throw new Error('Email tidak ditemukan');
    const token = jwt.sign({ email }, this.secret, { expiresIn: '1h' });
    await resetRepo.create({ email, token, expiresAt: new Date(Date.now() + 3600000) });
    return { token };
  }

  async resetPassword(token, password) {
    const record = await resetRepo.findByEmailAndToken('', token);
    if (!record) throw new Error('Token tidak valid atau expired');
    const hash = await bcrypt.hash(password, 12);
    await prisma.user.update({ where: { email: record.email }, data: { password: hash } });
    await resetRepo.markUsed(record.id);
    return true;
  }

  generateTokens(user) {
    const payload = { id: user.id, email: user.email, role: user.role };
    const accessToken = jwt.sign(payload, this.secret, { expiresIn: this.expiresIn });
    const refreshToken = jwt.sign({ id: user.id }, this.refreshSecret, { expiresIn: this.refreshExpiresIn });
    return { accessToken, refreshToken };
  }
}

export default new AuthService();
