package su.xserver.iikocon;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.ext.web.codec.BodyCodec;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;

public class ProxyVerticle extends AbstractVerticle {

  private WebClient webClient;

  @Override
  public void start(Promise<Void> startPromise) {
    webClient = WebClient.create(vertx, new WebClientOptions()
      .setSsl(true)
      .setTrustAll(true)
      .setVerifyHost(false));

    Router router = Router.router(vertx);
    router.post("/api/proxy").handler(this::handlePost);
    router.get("/api/proxy").handler(this::handleGet);

    int port = 8080;
    vertx.createHttpServer()
      .requestHandler(router)
      .listen(port).onComplete(http -> {
        if (http.succeeded()) {
          System.out.println("Proxy server started on port " + port);
          startPromise.complete();
        } else {
          startPromise.fail(http.cause());
        }
      });
  }

  private void handlePost(RoutingContext ctx) {
    String apiServer = System.getenv("IIKO_API_SERVER");
    String apiLogin = System.getenv("IIKO_API_LOGIN");
    String apiPass = System.getenv("IIKO_API_PASS");
    String externalEndpoint = System.getenv("IIKO_API_ENDPOINT");
    if (externalEndpoint == null || externalEndpoint.isBlank()) {
      externalEndpoint = "/your-endpoint";
    }

    if (apiServer == null || apiLogin == null || apiPass == null) {
      fail(ctx, 500, "Missing required environment variables: IIKO_API_SERVER, IIKO_API_LOGIN, IIKO_API_PASS");
      return;
    }

    JsonObject body = ctx.body().asJsonObject();
    if (body == null) {
      fail(ctx, 400, "Request body must be JSON");
      return;
    }

    String signature = sha1(apiPass);
    String authUrl = "https://" + apiServer + ":443/resto/api/auth?login=" + apiLogin + "&pass=" + signature;
    String finalExternalEndpoint = externalEndpoint;
    webClient.getAbs(authUrl)
      .as(BodyCodec.string())
      .send()
      .onSuccess(authResp -> {
        if (authResp.statusCode() != 200) {
          fail(ctx, authResp.statusCode(), "Authentication failed: " + authResp.statusMessage());
          return;
        }
        String token = authResp.body();
        String targetUrl = "https://" + apiServer + finalExternalEndpoint;
        webClient.request(HttpMethod.POST, targetUrl)
          .putHeader("Content-Type", "application/json")
          .as(BodyCodec.jsonObject())
          .sendJsonObject(body)
          .onSuccess(apiResp -> {
            webClient.getAbs("https://" + apiServer + ":443/resto/api/logout?key=" + token)
              .send()
              .onFailure(err -> System.err.println("Logout failed: " + err.getMessage()));
            if (apiResp.statusCode() == 200) {
              ctx.response().setStatusCode(200).end(apiResp.body().encode());
            } else {
              fail(ctx, apiResp.statusCode(), "External API error: " + apiResp.statusMessage());
            }
          })
          .onFailure(err -> fail(ctx, 500, "Request to external API failed: " + err.getMessage()));
      })
      .onFailure(err -> fail(ctx, 500, "Auth request failed: " + err.getMessage()));
  }

  private void handleGet(RoutingContext ctx) {
    String presetId = ctx.queryParam("presetId").stream().findFirst().orElse(null);
    String dateFrom = ctx.queryParam("dateFrom").stream().findFirst().orElse(null);
    String dateTo = ctx.queryParam("dateTo").stream().findFirst().orElse(null);
    String server = ctx.queryParam("server").stream().findFirst().orElse(null);
    String password = ctx.queryParam("password").stream().findFirst().orElse(null);
    String login = ctx.queryParam("login").stream().findFirst().orElse(null);
    String type = ctx.queryParam("type").stream().findFirst().orElse(null);
    String rootType = ctx.queryParam("rootType").stream().findFirst().orElse(null);

    if (server == null || login == null || password == null) {
      fail(ctx, 400, "Missing required parameters: server, login, password");
      return;
    }

    String signature = sha1(password);
    String authUrl = "https://" + server + ":443/resto/api/auth?login=" + login + "&pass=" + signature;
    webClient.getAbs(authUrl)
      .as(BodyCodec.string())
      .send()
      .onSuccess(authResp -> {
        if (authResp.statusCode() != 200) {
          fail(ctx, authResp.statusCode(), "Authentication failed: " + authResp.statusMessage());
          return;
        }
        String token = authResp.body();
        String dataUrl;
        if ("entity".equals(type)) {
          dataUrl = "https://" + server + "/resto/api/v2/entities/list?key=" + token;
          if (rootType != null && !rootType.isBlank()) {
            dataUrl += "&rootType=" + rootType;
          }
        } else {
          if (presetId == null || dateFrom == null || dateTo == null) {
            fail(ctx, 400, "Missing presetId, dateFrom or dateTo for report request");
            return;
          }
          dataUrl = "https://" + server + "/resto/api/v2/reports/olap/byPresetId/" + presetId +
            "?key=" + token + "&dateFrom=" + dateFrom + "&dateTo=" + dateTo;
        }
        System.out.println("URL: " + dataUrl);
        webClient.getAbs(dataUrl)
          .as(BodyCodec.jsonObject())
          .send()
          .onSuccess(dataResp -> {
            // logout (fire and forget)
            webClient.getAbs("https://" + server + ":443/resto/api/logout?key=" + token)
              .send()
              .onFailure(err -> System.err.println("Logout failed: " + err.getMessage()));
            if (dataResp.statusCode() == 200) {
              JsonObject responseBody = dataResp.body();
              if ("entity".equals(type)) {
                ctx.response().setStatusCode(200).end(responseBody.encode());
              } else {
                Object data = responseBody.getValue("data");
                if (data == null) {
                  ctx.response().setStatusCode(200).end(responseBody.encode());
                } else {
                  // data может быть массивом, объектом или другим типом
                  ctx.response().setStatusCode(200).end(Json.encode(data));
                }
              }
            } else {
              fail(ctx, dataResp.statusCode(), "External API error: " + dataResp.statusMessage());
            }
          })
          .onFailure(err -> fail(ctx, 500, "Data request failed: " + err.getMessage()));
      })
      .onFailure(err -> fail(ctx, 500, "Auth request failed: " + err.getMessage()));
  }

  private String sha1(String input) {
    try {
      MessageDigest md = MessageDigest.getInstance("SHA-1");
      byte[] digest = md.digest(input.getBytes());
      return HexFormat.of().formatHex(digest).toLowerCase();
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException(e);
    }
  }

  private void fail(RoutingContext ctx, int status, String message) {
    System.err.println("Error: " + message);
    ctx.response().setStatusCode(status).end(new JsonObject().put("error", message).encode());
  }
}

//  > GET /api/proxy?server=folk-amber-co.iiko.it&login=4444&password=4444&presetId=7ddc40c3-9d5f-408f-aa1e-652964b36c6c&dateFrom=2026-04-10&dateTo=2026-04-17 HTTP/1.1
//  > Host: localhost:8080
//  > access-token: ddb4ab653b9194ec1ea5448cee2a8a26282b0866c1d4a86e98e9b0f84bc91944
//  > User-Agent: v2raytun/ios
//  > X-App-Version: 2.4.3
//  > X-Device-Model: iPhone 11 Pro
//  > X-Device-OS: iOS
//  > X-HWID: HHS8JDJN-F2EB-HFBS-KMWX-234FA7B95JSC
//  > X-Ver-OS: 26.0
//  > Accept: */*
