package com.example.scheduler_server;

import java.io.*;
import java.net.*;
import java.util.Locale;

/*
    Name: UserThread
    Description: This class handles messages sent from the clients, and the required parsing to be used for in the
    Shift and Schedule classes.
 */
public class UserThread extends Thread {
    private final Socket socket;
    private final ScheduleServer server;
    private PrintWriter writer;
    private final DataBaseQuery dbQuery;

    /*
    Name: UserThread
    Parameters:
        Socket socket: socket for communicating to the client.
        ScheduleServer server: The class responsible for establishing the first connection to a new client and creating
            a UserThread for it.
        DataBaseQuery dbQuery: Class responsible for connecting to the database, and containing all the methods on it.
    Description: Constructor class that gets the input stream to be read when client messages are sent.
    Return: UserThread
     */
    public UserThread(Socket socket, ScheduleServer server, DataBaseQuery dbQuery) {
        this.socket = socket;
        this.server = server;
        this.dbQuery = dbQuery;
    }

    /*
    Name: Run
    Description: Responsible for handling and parsing messages from a client. This is also
    where the new user-thread begins.
     */
    public void run() {
        try {

            InputStream input = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(input));

            OutputStream output = socket.getOutputStream();
            writer = new PrintWriter(output, true);

            String serverMessage;
            String clientMessage;

            serverMessage = dbQuery.staff.allEmployees();
            sendMessage(serverMessage);
            serverMessage = dbQuery.schedule.allShifts();
            sendMessage(serverMessage);
            serverMessage = dbQuery.availability.allAvailabilities();
            sendMessage(serverMessage);
            serverMessage = dbQuery.timeOff.allTimeOff();
            sendMessage(serverMessage);
            serverMessage = dbQuery.positions.allPositions();
            sendMessage(serverMessage);

            do {
                clientMessage = reader.readLine();
                String[] args = clientMessage.split("/");
                System.out.println(clientMessage);
                switch (args[0]) {
                    case "addEmployee":
                        if(args.length == 7){
                            try {
                                String newEmployee = dbQuery.staff.addEmployee(args[1], args[2], args[3], args[4], args[5],
                                        Float.parseFloat(args[6]));
                                if (!newEmployee.isEmpty()){
                                    String newEmployeeAvailability = dbQuery.availability
                                            .newEmployeeAvailability(Integer.parseInt(newEmployee.split(",")[0]));
                                    server.broadcast("addEmployee/" + newEmployee);
                                    server.broadcast("newEmployeeAvailability/" + newEmployeeAvailability);
                                }
                            } catch (Exception exception) {
                                System.out.println("Error: Formatting exception while adding employee");
                                exception.printStackTrace();
                            }
                        }
                        break;
                    case "editEmployee":
                        if(args.length == 8){
                            try {
                                String editedEmployee = dbQuery.staff.editEmployee(Integer.parseInt(args[1]), args[2], args[3],
                                        args[4], args[5], args[6], Float.parseFloat(args[7]));
                                if (!editedEmployee.isEmpty()){
                                    server.broadcast("editEmployee/" + editedEmployee);
                                }
                            } catch (Exception exception) {
                                System.out.println("Error: Formatting exception while adding employee");
                                exception.printStackTrace();
                            }
                        }
                    case "removeEmployee":
                        try {
                            int employeeID = Integer.parseInt(args[1]);
                            dbQuery.schedule.removeAllShiftsByID(employeeID);
                            dbQuery.timeOff.removeAllTimeOffByID(employeeID);
                            dbQuery.availability.removeEmployeeAvailability(employeeID);
                            if (employeeID == dbQuery.staff.removeEmployee(employeeID)) {
                                server.broadcast("removeEmployee/" + employeeID);
                                server.broadcast("removeEmployeeAvailability/" + employeeID);
                                server.broadcast("removeAllShiftsByID/" + employeeID);
                                server.broadcast("removeAllTimeOffByID/" + employeeID);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while removing employee");
                            exception.printStackTrace();
                        }
                        break;
                    case "editShift":
                        try {
                            String editedShift = dbQuery.schedule.editShift(Integer.parseInt(args[1]), args[2], args[3],
                                    args[4], args[5], Integer.parseInt(args[6]));
                            if (!editedShift.isEmpty()) {
                                server.broadcast("editShift/" + editedShift);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while editing shift");
                            exception.printStackTrace();
                        }
                        break;
                    case "addShift":
                        try {
                            String newShift = dbQuery.schedule.addShift(Integer.parseInt(args[1]), args[2],
                                    Integer.parseInt(args[3]), Integer.parseInt(args[4]), args[5]);
                            if (!newShift.isEmpty()){
                                server.broadcast("addShift/" + newShift);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while adding shift");
                            exception.printStackTrace();
                        }
                        break;
                    case "editShiftAvailability":
                        try {
                            String editedShiftAvailability = dbQuery.schedule.editShiftAvailability(Integer.parseInt(args[1]), args[2]);
                            if (!editedShiftAvailability.isEmpty()) {
                                server.broadcast("editShiftAvailability/" + args[1] + "/" + args[2]);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "removeShift":
                        try {
                            int shiftID = Integer.parseInt(args[1]);
                            if (shiftID == dbQuery.schedule.removeShiftByID(shiftID)) {
                                server.broadcast("removeShift/" + shiftID);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while removing shift");
                            exception.printStackTrace();
                        }
                        break;
                    case "editAvailability":
                        try {
                            String editedAvailability = dbQuery.availability.editAvailability(Integer.parseInt(args[1]),
                                    Integer.parseInt(args[2]), Integer.parseInt(args[3]), Integer.parseInt(args[4]));
                            if (!editedAvailability.isEmpty()) {
                                server.broadcast("editedAvailability/" + editedAvailability);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while editing availability");
                            exception.printStackTrace();
                        }
                        break;
                    case "addTimeOff":
                        try {
                            String newTimeOff = dbQuery.timeOff.addTimeOff(Integer.parseInt(args[1]), args[2], args[3],
                                    false, args[4]);
                            if (!newTimeOff.isEmpty()) {
                                server.broadcast("addTimeOff/" + newTimeOff);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while add time off");
                            exception.printStackTrace();
                        }
                        break;
                    case "removeTimeOff":
                        try {
                            int timeOffID = Integer.parseInt(args[1]);
                            if (timeOffID == dbQuery.timeOff.removeTimeOff(Integer.parseInt(args[1]))) {
                                server.broadcast("removeTimeOff/" + timeOffID);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while removing time off");
                            exception.printStackTrace();
                        }
                        break;
                    case "setTimeOffApproval":
                        try {
                            String newTimeOffApproval = dbQuery.timeOff.setTimeOffApproval(Integer.parseInt(args[1]), args[2]);
                            if (!newTimeOffApproval.isEmpty()) {
                                server.broadcast("timeOffApproval/" + newTimeOffApproval);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "addPosition":
                        try {
                            String newPosition = dbQuery.positions.addPosition(args[1], Integer.parseInt(args[2]));
                            if (!newPosition.isEmpty()) {
                                server.broadcast("addPosition/" + newPosition);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "editPosition":
                        try {
                            String editedPosition = dbQuery.positions.editPosition(args[1], Float.parseFloat(args[2]));
                            if (!editedPosition.isEmpty()) {
                                server.broadcast("addPosition/" + editedPosition);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "removePosition":
                        try {
                            dbQuery.staff.removePositionAllEmployees(args[1]);
                            String removedPosition = dbQuery.positions.removePosition(args[1]);
                            if (removedPosition.equalsIgnoreCase(args[1])) {
                                server.broadcast("removePositionAllEmployees/" + args[1].toLowerCase(Locale.ROOT));
                                server.broadcast("removePosition/" + args[1].toLowerCase(Locale.ROOT));
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "addEmployeePosition":
                        try {
                            String addedPosition = dbQuery.staff.addPosition(Integer.parseInt(args[1]), args[2]);
                            if (!addedPosition.isEmpty()) {
                                server.broadcast("addEmployeePosition/" + addedPosition);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "removeEmployeePosition":
                        try {
                            String removedPosition = dbQuery.staff.removePosition(Integer.parseInt(args[1]), args[2]);
                            if (!removedPosition.isEmpty()) {
                                server.broadcast("removeEmployeePosition/" + removedPosition);
                            }
                        } catch (NumberFormatException exception) {
                            System.out.println("Error: Formatting exception while setting time off approval");
                            exception.printStackTrace();
                        }
                        break;
                    case "checkIn":
                        try {
                            int shiftID = Integer.parseInt(args[1]);
                            int checkedIn = dbQuery.schedule.checkIn(shiftID);
                            if (shiftID == checkedIn) {
                                server.broadcast("checkedIn/" + shiftID);
                            }
                        } catch (NumberFormatException exception) {
                            exception.printStackTrace();
                        }
                        break;
                    case "allEmployees":
                        //Returns all Employees
                        serverMessage = dbQuery.staff.allEmployees();
                        sendMessage(serverMessage);
                        break;
                    case "allShifts":
                        serverMessage = dbQuery.schedule.allShifts();
                        sendMessage(serverMessage);
                        break;
                    case "allAvailabilities":
                        serverMessage = dbQuery.availability.allAvailabilities();
                        sendMessage(serverMessage);
                        break;
                    case "allTimeOff":
                        serverMessage = dbQuery.timeOff.allTimeOff();
                        sendMessage(serverMessage);
                        break;

                }

            } while (!clientMessage.equals("logout"));

            server.removeUser(this);
            socket.close();

        } catch (IOException exception) {
            System.out.println("Error in UserThread: " + exception.getMessage());
            exception.printStackTrace();
        }
    }

    /*
    Name: sendMessage
    Parameters:
        String message: message to be sent.
    Description: Sends a message to the client of this thread.
    Return: void
     */
    void sendMessage(String message) {
        writer.println(message);
    }
}