package library.routes;

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

import io.javalin.config.JavalinConfig;
import io.javalin.http.Context;
import library.domain.Loan;
import library.domain.Member;
import library.dto.NewLoan;
import library.service.LoanService;

/**
 * HTTP in, HTTP out for /loans.
 *
 * Every handler passes the current member (from the session) to the service. A
 * loan that isn't yours is a 404, not a 403: we don't confirm it exists.
 */
public class LoanRoutes {

	private final LoanService service;

	public LoanRoutes(LoanService service) {
		this.service = service;
	}

	public void register(JavalinConfig config) {
		config.routes.get("/loans", ctx -> list(ctx));
		config.routes.get("/loans/{id}", ctx -> getOne(ctx));
		config.routes.post("/loans", ctx -> checkout(ctx));
		config.routes.put("/loans/{id}/return", ctx -> returnBook(ctx));
	}

	// GET /loans → a librarian gets every loan, a member only their own
	private void list(Context ctx) throws SQLException {
		Member current = Auth.currentMember(ctx);
		ctx.json(service.listLoans(current));
	}

	// GET /loans/{id}
	private void getOne(Context ctx) throws SQLException {
		Member current = Auth.currentMember(ctx);
		int id = Integer.parseInt(ctx.pathParam("id"));
		Optional<Loan> loan = service.findLoan(current, id);
		if (loan.isPresent()) {
			ctx.json(loan.get());
		} else {
			ctx.status(404);
		}
	}

	// POST /loans { "bookId": 3 } → 201. The borrower is the current member, never
	// the body.
	private void checkout(Context ctx) throws SQLException {
		Member current = Auth.currentMember(ctx);
		NewLoan body = ctx.bodyAsClass(NewLoan.class);
		if (body.bookId() == null) {
			ctx.status(400).result("bookId is required");
			return;
		}
		try {
			Loan created = service.checkOut(current.memberId(), body.bookId());
			ctx.status(201);
			ctx.header("Location", "/loans/" + created.loanId());
			ctx.json(created);
		} catch (NoSuchElementException e) {
			ctx.status(400).result(e.getMessage()); // no such book (the bookId in the body is bad)
		} catch (IllegalStateException e) {
			// Already checked out. The service's "inserted but can't be found" is also an
			// IllegalStateException and would land here too
			// tell the two apart.
			ctx.status(409).result(e.getMessage());
		}
	}

	// PUT /loans/{id}/return → 200 with the loan, no body needed. Idempotent.
	private void returnBook(Context ctx) throws SQLException {
		Member current = Auth.currentMember(ctx);
		int id = Integer.parseInt(ctx.pathParam("id"));
		Optional<Loan> loan = service.returnBook(current, id);
		if (loan.isPresent()) {
			ctx.json(loan.get());
		} else {
			ctx.status(404);
		}
	}
}
