package de.nowchess.backcore.resource

import de.nowchess.api.board.Square
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
import de.nowchess.backcore.dto.*
import de.nowchess.backcore.game.{GameMapper, GameService}
import jakarta.enterprise.context.ApplicationScoped
import jakarta.inject.Inject
import jakarta.ws.rs.*
import jakarta.ws.rs.core.{MediaType, Response}

@Path("/api/board/game")
@Produces(Array(MediaType.APPLICATION_JSON))
@ApplicationScoped
class MoveResource @Inject() (service: GameService):

  @POST
  @Path("/{gameId}/move/{uci}")
  def makeMove(
      @PathParam("gameId") gameId: String,
      @PathParam("uci") uci: String,
  ): Response =
    if !service.isKnownId(gameId) then
      Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found")).build()
    else
      service.applyMove(uci) match
        case Right(snap) => Response.ok(GameMapper.toGameState(snap)).build()
        case Left(err)   => Response.status(400).entity(ApiErrorResponse("INVALID_MOVE", err)).build()

  @GET
  @Path("/{gameId}/moves")
  def getLegalMoves(
      @PathParam("gameId") gameId: String,
      @QueryParam("square") squareParam: String,
  ): Response =
    if !service.isKnownId(gameId) then
      Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found")).build()
    else
      val square = Option(squareParam).flatMap(Square.fromAlgebraic)
      val moves  = service.legalMoves(square)
      val dtos   = moves.map(toLegalMoveDto)
      Response.ok(LegalMovesResponse(dtos)).build()

  @POST
  @Path("/{gameId}/undo")
  def undoMove(@PathParam("gameId") gameId: String): Response =
    if !service.isKnownId(gameId) then
      Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found")).build()
    else
      service.undo() match
        case Right(snap) => Response.ok(GameMapper.toGameState(snap)).build()
        case Left(err)   => Response.status(400).entity(ApiErrorResponse("UNDO_NOT_AVAILABLE", err)).build()

  @POST
  @Path("/{gameId}/redo")
  def redoMove(@PathParam("gameId") gameId: String): Response =
    if !service.isKnownId(gameId) then
      Response.status(404).entity(ApiErrorResponse("GAME_NOT_FOUND", s"Game $gameId not found")).build()
    else
      service.redo() match
        case Right(snap) => Response.ok(GameMapper.toGameState(snap)).build()
        case Left(err)   => Response.status(400).entity(ApiErrorResponse("REDO_NOT_AVAILABLE", err)).build()

  private def toLegalMoveDto(move: Move): LegalMoveDto =
    val uci = GameMapper.moveToUci(move)
    val (moveType, promotion) = move.moveType match
      case MoveType.Normal(true)    => ("capture", None)
      case MoveType.Normal(false)   => ("normal", None)
      case MoveType.CastleKingside  => ("castleKingside", None)
      case MoveType.CastleQueenside => ("castleQueenside", None)
      case MoveType.EnPassant       => ("enPassant", None)
      case MoveType.Promotion(pp) =>
        val pName = pp match
          case PromotionPiece.Queen  => "queen"
          case PromotionPiece.Rook   => "rook"
          case PromotionPiece.Bishop => "bishop"
          case PromotionPiece.Knight => "knight"
        ("promotion", Some(pName))
    LegalMoveDto(
      from = move.from.toString,
      to = move.to.toString,
      uci = uci,
      moveType = moveType,
      promotion = promotion,
    )
