package library.service;

import java.sql.SQLException;
import java.util.List;
import java.util.Optional;

import org.mindrot.jbcrypt.BCrypt;

import library.dao.MemberDao;
import library.domain.Member;
import library.domain.Roles;
import library.dto.NewMember;

/**
 * Registering and logging in members.
 */
public class MemberService {

	private final MemberDao memberDao;
	private final SessionService sessionService;

	public MemberService(MemberDao memberDao, SessionService sessionService) {
		this.memberDao = memberDao;
		this.sessionService = sessionService;
	}

	// POST /members
	// A duplicate username still gives a 500 (UNIQUE, error 1062)
	public Member register(NewMember nm) throws SQLException {
		if (nm.username() == null || nm.username().isBlank()) {
			throw new IllegalArgumentException("username is required");
		}
		if (nm.password() == null || nm.password().isEmpty()) {
			throw new IllegalArgumentException("password is required");
		}
		// The plaintext exists only long enough to hash; it's never stored or logged
		String hash = BCrypt.hashpw(nm.password(), BCrypt.gensalt());
		int id = memberDao.insert(nm.username(), hash, Roles.MEMBER); // role set by the server
		Optional<Member> created = memberDao.findById(id);
		if (created.isEmpty()) {
			throw new IllegalStateException("Member " + id + " was inserted but can't be found");
		}
		return created.get();
	}

	// POST /login → a new session token, or empty for a wrong username OR a wrong
	// password.
	// Both failures look the same so the response doesn't reveal which usernames
	// exist.
	public Optional<String> login(String username, String password) throws SQLException {
		if (username == null || password == null) {
			return Optional.empty();
		}
		Optional<Member> member = memberDao.findByUsername(username);
		if (member.isEmpty() || !BCrypt.checkpw(password, member.get().passwordHash())) {
			return Optional.empty();
		}
		return Optional.of(sessionService.start(member.get()));
	}

	// GET /members (librarians only; the route checks the role)
	public List<Member> findAll() throws SQLException {
		return memberDao.findAll();
	}
}
