import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import crypto from 'node:crypto';
import { z } from 'zod';
import * as usersRepo from '../repositories/users.repository.js';
import * as tokensRepo from '../repositories/tokens.repository.js';
import { TokenType } from '../generated/prisma/enums.js';

const JWT_SECRET = process.env.JWT_SECRET ?? 'changeme';
const JWT_EXPIRES_IN = '15m';
const REFRESH_EXPIRES_DAYS = 7;

export const registerSchema = z.object({
  name: z.string().check(z.minLength(2)),
  email: z.email(),
  password: z.string().check(z.minLength(6)),
});

export const loginSchema = z.object({
  email: z.email(),
  password: z.string().check(z.minLength(1)),
});

export async function register(input: z.infer<typeof registerSchema>) {
  const existing = await usersRepo.findUserByEmail(input.email);
  if (existing) throw new Error('Email already in use');

  const hashed = await bcrypt.hash(input.password, 10);
  const now = new Date();

  const user = await usersRepo.createUser({
    id: crypto.randomUUID(),
    name: input.name,
    email: input.email,
    password: hashed,
    updatedAt: now,
  });

  return { id: user.id, name: user.name, email: user.email };
}

export async function login(input: z.infer<typeof loginSchema>) {
  const user = await usersRepo.findUserByEmail(input.email);
  if (!user) throw new Error('Invalid credentials');

  const valid = await bcrypt.compare(input.password, user.password);
  if (!valid) throw new Error('Invalid credentials');

  const accessToken = jwt.sign({ sub: user.id }, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });

  const refreshToken = crypto.randomUUID();
  const expiresAt = new Date();
  expiresAt.setDate(expiresAt.getDate() + REFRESH_EXPIRES_DAYS);

  await tokensRepo.createToken({
    id: crypto.randomUUID(),
    token: refreshToken,
    type: TokenType.REFRESH,
    userId: user.id,
    expiresAt,
  });

  return { accessToken, refreshToken };
}

export async function logout(refreshToken: string) {
  await tokensRepo.deleteToken(refreshToken);
}

export function verifyAccessToken(token: string): { sub: string } {
  return jwt.verify(token, JWT_SECRET) as { sub: string };
}