package web;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.Javalin;
import io.javalin.http.HttpStatus;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import somecodes.Codes;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;

import static io.javalin.apibuilder.ApiBuilder.*;

@SuppressWarnings("unused")
public class webApp {
    private final Javalin app;
    private final String listenAddress;
    private final int listenPort;
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final Logger logger = LoggerFactory.getLogger(webApp.class);
    private final Map<String, String> usermap = new HashMap<>();
    public webApp(String listenAddress, int listenPort, BiFunction<String,WsCommand, WsReply> callback, Pair<String, String>... users ) {
        this.listenAddress = listenAddress;
        this.listenPort = listenPort;
        if (users != null){
            for (Pair<String, String> user : users) {
                this.usermap.put(user.getKey(), user.getValue());
            }
        }
        app = Javalin.create(config -> {
            config.useVirtualThreads = true; // Enable virtual threads for better performance
            config.staticFiles.add("/");
            config.router.apiBuilder(()->{
                path("/", () -> get(ctx -> {
                    if (ctx.sessionAttribute("user") == null) {
                        ctx.redirect("/index.html");
                    } else {
                        ctx.redirect("/pocreceiver.html");
                    }
                }));
                path("/index.html", ()-> {
                    before( ctx ->{
                        ctx.sessionAttribute("user", null); // Clear user session on index page
                    });
                    post(ctx-> {
                        var username = ctx.formParam("username");
                        var password = ctx.formParam("password");
                        var xx = usermap.get(username);
                        if (xx != null && xx.equals(password)) {
                            ctx.sessionAttribute("user", username);
                            ctx.redirect("/pocreceiver.html");
                        } else {
                            ctx.result("Invalid username or password");
                        }
                    });
                });
                path("/pocreceiver.html", ()->{
                    before(ctx -> {
                        if (ctx.sessionAttribute("user") == null) {
                            ctx.redirect("/index.html");
                        }
                    });
                    ws("/ws", wshandler -> wshandler.onMessage(wsMessageContext -> {
                        // Handle incoming WebSocket messages
                        String message = wsMessageContext.message();
                        try{
                            var command = objectMapper.readValue(message, WsCommand.class);
                            //logger.info("Received command from pocreceiver.html/ws : {}", command);
                            var reply = callback.apply("pocreceiver", command);
                            wsMessageContext.send(reply);
                        } catch (Exception e){
                            logger.error("Error processing {} from pocreceiver.html/ws: {}",message, e.getMessage());
                        }
                    }));
                });
                path("/prerecordedbroadcast.html", ()->{
                    before(ctx -> {
                        if (ctx.sessionAttribute("user") == null) {
                            ctx.redirect("/index.html");
                        }
                    });
                    ws("/ws", wshandler -> wshandler.onMessage(wsMessageContext -> {
                        // Handle incoming WebSocket messages
                        String message = wsMessageContext.message();
                        try{
                            var command = objectMapper.readValue(message, WsCommand.class);
                            //logger.info("Received command from prerecordedbroadcast.html/ws : {}", command);
                            var reply = callback.apply("prerecordedbroadcast", command);
                            wsMessageContext.send(reply);
                        } catch (Exception e){
                            logger.error("Error processing {} from prerecordedbroadcast.html/ws: {}",message, e.getMessage());
                        }
                    }));
                });
               path("/setting.html", () ->{
                    before(ctx -> {
                        if (ctx.sessionAttribute("user") == null) {
                            ctx.redirect("/index.html");
                        }
                    });
                    post("/upload", ctx -> {
                        // Handle file upload
                        var file = ctx.uploadedFile("file");
                        if (file != null ) {
                            // Process the uploaded file
                            try(InputStream in = file.content()) {
                                var savetarget = Codes.Companion.getAudioFilePath().resolve(file.filename());
                                Files.copy(in, savetarget, StandardCopyOption.REPLACE_EXISTING);
                                logger.info("File uploaded: {}", file.filename());
                                ctx.status(HttpStatus.OK).result("File uploaded successfully: " + file.filename());
                            } catch (Exception e){
                                ctx.status(HttpStatus.INTERNAL_SERVER_ERROR).result("File upload failed: " + file.filename());
                            }

                        } else {
                            ctx.status(HttpStatus.BAD_REQUEST).result("No file uploaded");
                        }
                    });
                   ws("/ws", wshandler -> wshandler.onMessage(wsMessageContext -> {
                       // Handle incoming WebSocket messages
                       String message = wsMessageContext.message();
                       //logger.info("Received message from setting.html/ws: {}", message);
                       try{
                           var command = objectMapper.readValue(message, WsCommand.class);
                           //logger.info("Received command from setting.html/ws : {}", command);
                           var reply = callback.apply("setting", command);
                           //logger.info("Replying to setting.html/ws : {}", reply);
                           wsMessageContext.send(reply);
                       } catch (Exception e){
                            logger.error("Error processing {} from setting.html/ws: {}", message, e.getMessage());
                       }


                   }));
                });

            });
        });

    }

    /**
     * Start the web application.
     */
    public boolean Start(){
        try{
            app.start(listenAddress, listenPort);
            return true;
        } catch (Exception e){
            logger.error("webApp start failed", e.getMessage());
        }
        return false;

    }

    /**
     * Stop the web application.
     */
    public void Stop(){
        try{
            app.stop();
            logger.info("webApp stopped");
        } catch (Exception e){
            logger.error("webApp stop failed", e.getMessage());
        }

    }
}
