// Copyright (C) Call of Nil contributors
// SPDX-License-Identifier: AGPL-3.0-only

#![expect(clippy::wildcard_imports)]

mod auth;
mod battle;
mod chat;
mod cheat;
mod city;
mod continent;
mod infrastructure;
mod military;
mod npc;
mod player;
mod ranking;
mod report;
mod round;
mod server;
mod user;
mod websocket;
mod world;

use crate::app::App;
use crate::middleware::authorization::authorization;
use axum::http::{Method, StatusCode};
use axum::response::Redirect;
use axum::routing::{any, get, post, put};
use axum::{Router, middleware};
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::{DefaultMakeSpan, DefaultOnFailure, DefaultOnResponse, TraceLayer};
use tracing::Level;

#[expect(clippy::too_many_lines)]
pub(crate) fn create() -> Router<App> {
  #[rustfmt::skip]
  let router = Router::new()
    .route("/add-academy-recruit-order", post(infrastructure::academy::add_recruit_order))
    .route("/add-prefecture-build-order", post(infrastructure::prefecture::add_build_order))
    .route("/add-stable-recruit-order", post(infrastructure::stable::add_recruit_order))
    .route("/add-workshop-recruit-order", post(infrastructure::workshop::add_recruit_order))
    .route("/cancel-academy-recruit-order", post(infrastructure::academy::cancel_recruit_order))
    .route("/cancel-prefecture-build-order", post(infrastructure::prefecture::cancel_build_order))
    .route("/cancel-stable-recruit-order", post(infrastructure::stable::cancel_recruit_order))
    .route("/cancel-workshop-recruit-order", post(infrastructure::workshop::cancel_recruit_order))
    .route("/cheat-get-academy-recruit-queue", put(cheat::infrastructure::get_academy_recruit_queue))
    .route("/cheat-get-academy-recruit-queues", put(cheat::infrastructure::get_academy_recruit_queues))
    .route("/cheat-get-all-academy-recruit-queues", put(cheat::infrastructure::get_all_academy_recruit_queues))
    .route("/cheat-get-all-prefecture-build-queues", put(cheat::infrastructure::get_all_prefecture_build_queues))
    .route("/cheat-get-all-stable-recruit-queues", put(cheat::infrastructure::get_all_stable_recruit_queues))
    .route("/cheat-get-build-steps", put(cheat::behavior::get_build_steps))
    .route("/cheat-get-ethics", put(cheat::npc::get_ethics))
    .route("/cheat-get-idle-armies-at", put(cheat::military::get_idle_armies_at))
    .route("/cheat-get-idle-personnel-at", put(cheat::military::get_idle_personnel_at))
    .route("/cheat-get-infrastructure", put(cheat::infrastructure::get_infrastructure))
    .route("/cheat-get-maneuvers", put(cheat::military::get_maneuvers))
    .route("/cheat-get-maneuvers-of", put(cheat::military::get_maneuvers_of))
    .route("/cheat-get-player", put(cheat::player::get_player))
    .route("/cheat-get-players", put(cheat::player::get_players))
    .route("/cheat-get-prefecture-build-queue", put(cheat::infrastructure::get_prefecture_build_queue))
    .route("/cheat-get-prefecture-build-queues", put(cheat::infrastructure::get_prefecture_build_queues))
    .route("/cheat-get-resources", put(cheat::resources::get_resources))
    .route("/cheat-get-stable-recruit-queue", put(cheat::infrastructure::get_stable_recruit_queue))
    .route("/cheat-get-stable-recruit-queues", put(cheat::infrastructure::get_stable_recruit_queues))
    .route("/cheat-get-storage-capacity", put(cheat::infrastructure::get_storage_capacity))
    .route("/cheat-set-bot-ethics", post(cheat::npc::set_bot_ethics))
    .route("/cheat-set-building-level", post(cheat::infrastructure::set_building_level))
    .route("/cheat-set-food", post(cheat::resources::set_food))
    .route("/cheat-set-iron", post(cheat::resources::set_iron))
    .route("/cheat-set-max-food", post(cheat::resources::set_max_food))
    .route("/cheat-set-max-infrastructure", post(cheat::infrastructure::set_max_infrastructure))
    .route("/cheat-set-max-iron", post(cheat::resources::set_max_iron))
    .route("/cheat-set-max-resources", post(cheat::resources::set_max_resources))
    .route("/cheat-set-max-silo-resources", post(cheat::resources::set_max_silo_resources))
    .route("/cheat-set-max-stone", post(cheat::resources::set_max_stone))
    .route("/cheat-set-max-warehouse-resources", post(cheat::resources::set_max_warehouse_resources))
    .route("/cheat-set-max-wood", post(cheat::resources::set_max_wood))
    .route("/cheat-set-resources", post(cheat::resources::set_resources))
    .route("/cheat-set-stability", post(cheat::city::set_stability))
    .route("/cheat-set-stone", post(cheat::resources::set_stone))
    .route("/cheat-set-wood", post(cheat::resources::set_wood))
    .route("/cheat-skip-round", post(cheat::round::skip))
    .route("/cheat-spawn-bot", post(cheat::npc::spawn_bot))
    .route("/cheat-spawn-personnel", post(cheat::military::spawn_personnel))
    .route("/create-remote-world", post(world::remote::create))
    .route("/delete-remote-world", post(world::remote::delete))
    .route("/forward-report", post(report::forward))
    .route("/get-academy-recruit-catalog", put(infrastructure::academy::get_recruit_catalog))
    .route("/get-chat-history", put(chat::get))
    .route("/get-city", put(city::get_city))
    .route("/get-player", put(player::get_player))
    .route("/get-player-maintenance", put(player::get_maintenance))
    .route("/get-player-military", put(player::get_military))
    .route("/get-player-storage-capacity", put(player::get_storage_capacity))
    .route("/get-prefecture-build-catalog", put(infrastructure::prefecture::get_build_catalog))
    .route("/get-stable-recruit-catalog", put(infrastructure::stable::get_recruit_catalog))
    .route("/get-workshop-recruit-catalog", put(infrastructure::workshop::get_recruit_catalog))
    .route("/leave", post(world::leave))
    .route("/push-chat-message", post(chat::push))
    .route("/rename-city", post(city::rename_city))
    .route("/request-maneuver", post(military::request_maneuver))
    .route("/save-local-world", post(world::local::save))
    .route("/search-city", put(city::search_city))
    .route("/set-player-ready", post(round::set_ready))
    .route("/set-player-status", post(player::set_status))
    .route("/spawn-player", post(player::spawn))
    .route("/start-round", post(round::start))
    .route("/toggle-building", post(infrastructure::building::toggle))
    .route("/websocket", any(websocket::websocket))
    .route_layer(middleware::from_fn(authorization))

    // These don't need authorization.
    .route("/authorize", post(auth::authorize))
    .route("/create-user", post(user::create))
    .route("/get-bot-coords", put(npc::bot::get_coords))
    .route("/get-city-score", put(city::get_city_score))
    .route("/get-continent-size", put(continent::size))
    .route("/get-player-coords", put(player::get_coords))
    .route("/get-player-ids", put(player::get_ids))
    .route("/get-player-status", put(player::get_status))
    .route("/get-player-worlds", put(player::get_worlds))
    .route("/get-precursor-coords", put(npc::precursor::get_coords))
    .route("/get-public-bot", put(npc::bot::get_public_bot))
    .route("/get-public-bots", put(npc::bot::get_public_bots))
    .route("/get-public-cities", put(city::get_public_cities))
    .route("/get-public-city", put(city::get_public_city))
    .route("/get-public-field", put(continent::get_public_field))
    .route("/get-public-fields", put(continent::get_public_fields))
    .route("/get-public-player", put(player::get_public_player))
    .route("/get-public-players", put(player::get_public_players))
    .route("/get-public-precursor", put(npc::precursor::get_public_precursor))
    .route("/get-public-precursors", put(npc::precursor::get_public_precursors))
    .route("/get-rank", put(ranking::get_rank))
    .route("/get-ranking", put(ranking::get))
    .route("/get-remote-world", put(world::remote::get))
    .route("/get-remote-world-limit", get(world::remote::get_limit))
    .route("/get-remote-world-limit-per-user", get(world::remote::get_limit_per_user))
    .route("/get-round", put(round::get))
    .route("/get-server-kind", get(server::kind))
    .route("/get-world-bots", put(world::get_bots))
    .route("/get-world-config", put(world::get_config))
    .route("/get-world-personnel", put(world::get_personnel))
    .route("/get-world-players", put(world::get_players))
    .route("/get-world-precursors", put(world::get_precursors))
    .route("/get-world-stats", put(world::get_stats))
    .route("/player-exists", put(player::exists))
    .route("/search-public-city", put(city::search_public_city))
    .route("/simulate-battle", put(battle::simulate))
    .route("/user-exists", put(user::exists))
    .route("/validate-token", put(auth::validate_token))

    // Files.
    .route("/license", any(license))
    .route("/robots.txt", any(robots_txt));

  router.merge(with_cors()).layer(
    TraceLayer::new_for_http()
      .make_span_with(DefaultMakeSpan::new().include_headers(true))
      .on_response(DefaultOnResponse::new().level(Level::TRACE))
      .on_failure(DefaultOnFailure::new().level(Level::TRACE))
      .on_body_chunk(())
      .on_eos(())
      .on_request(()),
  )
}

fn with_cors() -> Router<App> {
  let cors = CorsLayer::new()
    .allow_methods([Method::GET])
    .allow_origin(Any);

  #[rustfmt::skip]
  let router = Router::new()
    .route("/", get(ok))
    .route("/get-remote-worlds", get(world::remote::get_all))
    .route("/version", get(server::version))
    .route_layer(cors);

  router
}

async fn ok() -> StatusCode {
  StatusCode::OK
}

async fn license() -> Redirect {
  Redirect::to("https://spdx.org/licenses/AGPL-3.0-only.html")
}

async fn robots_txt() -> &'static str {
  "User-agent: *\nDisallow: /"
}
