package com.expensetracker.api.auth.service;

import com.expensetracker.api.auth.dto.AuthResponse;
import com.expensetracker.api.auth.dto.LoginRequest;
import com.expensetracker.api.auth.dto.RefreshTokenRequest;
import com.expensetracker.api.auth.dto.RegisterRequest;
import com.expensetracker.api.security.service.JwtService;
import com.expensetracker.api.security.service.RefreshTokenService;
import com.expensetracker.api.user.model.User;
import com.expensetracker.api.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

@Service
@RequiredArgsConstructor
public class AuthService {

  private final UserRepository users;
  private final PasswordEncoder passwordEncoder;
  private final JwtService jwtService;
  private final RefreshTokenService refreshTokenService;

  public void register(RegisterRequest req) {
    String normalizedEmail = req.email().toLowerCase().trim();

    if (users.existsByEmail(normalizedEmail)) {
      throw new ResponseStatusException(HttpStatus.CONFLICT, "Email already registered");
    }

    User u = User.builder()
      .email(normalizedEmail)
      .passwordHash(passwordEncoder.encode(req.password()))
      .build();

    try {
      users.save(u);
    } catch (DataIntegrityViolationException e) {
      // Covers race conditions and case-variant duplicates.
      throw new ResponseStatusException(HttpStatus.CONFLICT, "Email already registered", e);
    }
  }

  public AuthResponse login(LoginRequest req) {
    String normalizedEmail = req.email().toLowerCase().trim();

    User u = users.findByEmail(normalizedEmail)
      .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid credentials"));

    if (!passwordEncoder.matches(req.password(), u.getPasswordHash())) {
      throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Invalid credentials");
    }

    return issueTokens(u);
  }

  public AuthResponse refresh(RefreshTokenRequest req) {
    RefreshTokenService.RotationResult rotation = refreshTokenService.rotate(req.refreshToken());
    String accessToken = jwtService.createAccessToken(rotation.user().getId());
    return new AuthResponse(
        accessToken,
        rotation.refreshToken(),
        "Bearer",
        jwtService.getAccessTokenExpiresInSeconds());
  }

  public void logout(RefreshTokenRequest req) {
    refreshTokenService.revoke(req.refreshToken());
  }

  private AuthResponse issueTokens(User user) {
    String accessToken = jwtService.createAccessToken(user.getId());
    String refreshToken = refreshTokenService.createToken(user);
    return new AuthResponse(
        accessToken,
        refreshToken,
        "Bearer",
        jwtService.getAccessTokenExpiresInSeconds());
  }
}
