package library.routes;

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

import io.javalin.config.JavalinConfig;
import io.javalin.http.Context;
import library.domain.Book;
import library.domain.Roles;
import library.dto.NewBook;
import library.service.BookService;

/**
 * HTTP in, HTTP out for /books.
 *
 * Each handler parses the request, calls one service method, and turns the
 * result into a status code and JSON. No SQL here.
 */
public class BookRoutes {

	private final BookService service;

	public BookRoutes(BookService service) {
		this.service = service;
	}

	// Javalin 7: called from inside Javalin.create(config -> { ... })
	public void register(JavalinConfig config) {
		config.routes.get("/books", ctx -> list(ctx));
		config.routes.get("/books/{id}", ctx -> getOne(ctx));
		config.routes.post("/books", ctx -> create(ctx)); // librarians only
		config.routes.put("/books/{id}", ctx -> update(ctx)); // librarians only
		config.routes.delete("/books/{id}", ctx -> delete(ctx)); // librarians only
	}

	// GET /books and GET /books?author=...
	private void list(Context ctx) throws SQLException {
		String author = ctx.queryParam("author");
		List<Book> books = (author == null) ? service.findAll() : service.findByAuthor(author);
		ctx.json(books);
	}

	// GET /books/{id}
	// A non-numeric id (GET /books/abc) still gives a 500 here
	private void getOne(Context ctx) throws SQLException {
		int id = Integer.parseInt(ctx.pathParam("id"));
		Optional<Book> book = service.findById(id);
		if (book.isPresent()) {
			ctx.json(book.get());
		} else {
			ctx.status(404);
		}
	}

	// POST /books
	// A duplicate ISBN still gives a 500 here
	private void create(Context ctx) throws SQLException {
		Auth.requireRole(ctx, Roles.LIBRARIAN);
		NewBook body = ctx.bodyAsClass(NewBook.class);
		try {
			Book created = service.create(body);
			ctx.status(201);
			ctx.header("Location", "/books/" + created.bookId());
			ctx.json(created);
		} catch (IllegalArgumentException e) {
			ctx.status(400).result(e.getMessage());
		}
	}

	// PUT /books/{id}: replaces the book's details (not its availability)
	private void update(Context ctx) throws SQLException {
		Auth.requireRole(ctx, Roles.LIBRARIAN);
		int id = Integer.parseInt(ctx.pathParam("id"));
		NewBook body = ctx.bodyAsClass(NewBook.class);
		try {
			Optional<Book> updated = service.update(id, body);
			if (updated.isPresent()) {
				ctx.json(updated.get());
			} else {
				ctx.status(404);
			}
		} catch (IllegalArgumentException e) {
			ctx.status(400).result(e.getMessage());
		}
	}

	// DELETE /books/{id} → 204
	// A book with loans still gives a 500 here (error 1451)
	private void delete(Context ctx) throws SQLException {
		Auth.requireRole(ctx, Roles.LIBRARIAN);
		int id = Integer.parseInt(ctx.pathParam("id"));
		if (service.delete(id)) {
			ctx.status(204);
		} else {
			ctx.status(404);
		}
	}
}
