import java.util.InputMismatchException;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Comparator;


// Main Building contains -> Rooms -> Guests -> Booking
// Contains hotel displayINFO Method


abstract class Hotel {
    final private static String hotelName = "Grand";
    final private static String hotelAddress = "123 Abdo Basha MainStreet ElAbsyia";
    final private static String hotelTele = "+010102100425";
    Scanner userIn = new Scanner(System.in);

    final public static String getHotelName() {
        return "Name: " + hotelName;
    }

    final public static String getHotelAddress() {
        return "address: " + hotelAddress;
    }

    final public static String getHotelTele() {
        return "Tele: " + hotelTele;
    }

    final public static void displayInfo(){
        System.out.println("---HOTEL INFO---");
        System.out.println(getHotelName());
        System.out.println(getHotelAddress());
        System.out.println(getHotelTele());

    }




}


/**
 * NEW ROOM CLASS TO FIX DUPLICATE ROOM NUMBER ISSUE
 * Each Room has a unique number based on its floor and index.
 * Also stores whether it's a Single or Double room and if it's occupied.
 */



class Room {
    int floor; // Floor index (0-based)
    int index; // Room index on the floor (0-based)
    boolean isDouble; // Is this a double room?
    boolean isOccupied; // Has someone booked this room?

    public Room(int floor, int index, boolean isDouble) { //constractor will be used later to create new rooms with the 3 arguments
        //
        this.floor = floor;
        this.index = index;
        this.isDouble = isDouble;
        this.isOccupied = false;
    }

    /**
     * Generates a room number like 101, 202, etc.
     * @return Unique room number
     */
    public int getRoomNumber() {
        return (floor + 1) * 100 + (index + 1);
    }

    @Override
    public String toString() {
        return "Room " + getRoomNumber() + " (" + (isDouble ? "Double" : "Single") + ") - " +
                (isOccupied ? "Occupied" : "Available");
    }
}

// this is the actuall room class which contains all rooms array type Room objects from above class
// 9 single rooms and 18 double rooms
// Odd room number [even index] -> Double
// even room number [odd index] -> single
// this is the actuall room class which contains all rooms array type Room objects from above class
// 9 single rooms and 18 double rooms
// Odd room number [even index] -> Double
// even room number [odd index] -> single
class Rooms extends Hotel {
    // *BUG FIX* removal of the old two arrays one for double and one for single
    // replace them by allRooms arraylist Using type Room Class
    private ArrayList<Room> allRooms = new ArrayList<>();

    public Rooms() {
        // Initialize hotel with 9 floors, 3 rooms per floor
        for (int floor = 0; floor < 9; floor++) { // changed it to 9 to handle 9 single guests case [i know it's not ideal but i am solo coding :)])
            for (int i = 0; i < 3; i++) {
                // Alternate between single and double rooms
                allRooms.add(new Room(floor, i, i % 2 == 0)); // even index = double room
            }
        }
    }

    // Check availability of rooms based on type
    public boolean checkAvailability(boolean isDouble) {
        for (Room room : allRooms) {
            if (room.isDouble == isDouble && !room.isOccupied) {
                return true;
            }
        }
        return false;
    }

    // Get an available room and mark it as occupied //occupy the avaliable room
    public int getAvailableRoom(boolean isDouble) {
        for (Room room : allRooms) {
            if (room.isDouble == isDouble && !room.isOccupied) {
                room.isOccupied = true;
                return room.getRoomNumber(); // Returns unique room number
            }
        }
        return -1; // No room found
    }

    // My code logic [loop that start from lowest room number] didn't need sorting but it was required and to be 100% sure rooms will be sorted when i use the hotel system
    public void sortRooms() {
        allRooms.sort(Comparator.comparingInt(Room::getRoomNumber)); // sort based on room number [ascending order]
    }

    // Display current status of all rooms
    public void displayRoomStatus() {
        sortRooms();
        System.out.println("\n--- ROOM STATUS ---");
        for (Room room : allRooms) {
            System.out.println(room);
        }
    }

    /**
     * NEW METHOD:
     * Free up a specific room by its room number.
     * Converts room number back to floor and room index.
     *
     * @param roomNumber The full room number (e.g., 101, 202)
     * @return true if successful, false otherwise
     */

    //Used in CheckOut Method ->Make the room cleared [Avaliable Again]
    //by looping through the array and choose the chosen room number if the FloorIndex && RoomIndex match
    //change the isOccupied value to false

    public boolean freeRoom(int roomNumber) {
        int floorIndex = ((roomNumber / 100) - 1); // e.g., 101 → 0, 201 → 1
        int roomIndex = (roomNumber % 100) - 1;   // e.g., 101 → 0, 102 → 1

        if (floorIndex < 0 || floorIndex >= 9 || roomIndex < 0 || roomIndex >= 3) {
            System.out.println("Invalid room number.");
            return false;
        }

        boolean isDouble = (roomIndex % 2 == 0); // even index = double room

        for (Room r : allRooms) {
            if (r.floor == floorIndex && r.index == roomIndex) {
                if (r.isOccupied) {
                    r.isOccupied = false;
                    System.out.println("Room " + roomNumber + " has been successfully vacated.");
                    return true;
                } else {
                    System.out.println("Room " + roomNumber + " is already vacant.");
                    return false;
                }
            }
        }
        System.out.println("Room not found.");
        return false;
    }
}


/**
 * Guests class handles number of guests and discounts
 */
class Guests extends Rooms {
    private int numberOfGuests;
    private boolean hasDiscount = false;

    public void setHasDiscount(boolean hasDiscount) {
        this.hasDiscount = hasDiscount;
    }

    public boolean hasDiscount() {
        return hasDiscount;
    }

    public void setNumberOfGuests(int numberOfGuests) {
        if (numberOfGuests == 0){
            System.out.println("Please enter a valid number, Restart the APP!");
            System.exit(0);

        } else if (numberOfGuests > 9) {
            System.out.println("For more the 9 guests please speak to our sales manger!");
            System.exit(0);

        } else {
            this.numberOfGuests = numberOfGuests;
        }
    }

    public int getNumberOfGuests() {
        return numberOfGuests;
    }

    // Collect guest info including number of guests and discount used in book() method
    public void collectGuestInfo() {
        System.out.print("Please enter the number of guests(min: 1 - max: 9) : ");
        setNumberOfGuests(userIn.nextInt());
        userIn.nextLine();
//        try {// Clear buffer
            System.out.print("Do you have a discount coupon? [You need to verify by showing your coupon] (y/n): ");
            String discountAnswer = userIn.nextLine().trim().toLowerCase();
            setHasDiscount(discountAnswer.equals("y"));
//        }
//        catch (InputMismatchException e){
//            System.out.println("Invalid input, Please restart the app");
//        }
    }
}

/**
 * Booking main system of the Hotel handles all functionalities from before because it inherited from all
 */
class Booking extends Guests {
    private boolean hasBooked = false;
    private int roomNumber;
    private double totalPrice;
    protected int nightsStayed = 0; // Tracks how many nights user will stay [added because logically one who stayed 10 days shouldn't be charged the same as ONE NIGHT]
    private ArrayList<Integer> bookedRooms = new ArrayList<>(); // Stores all booked room numbers //type integer because it only stores booked room numbers
    private HashMap<Integer, Boolean> roomTypes = new HashMap<>(); // Store room number and its type //Why use HashMap?? and What it is???
//how hash map work hashmap <key,value> name ----> the key here being the room number and it also holds wether this room number is single or double
    public void book() {
        collectGuestInfo();

        // Ask how many nights the guest will stay
        System.out.print("How many nights will you be staying? ");
        nightsStayed = userIn.nextInt();
        /// ////////////////////////


        /// //////////

        userIn.nextLine(); // Clear buffer as i got taught in Labs it helps avoid errors

        //Room Allocatiton and its handling giving all the possible options for you

        int numSingleRooms = 0;
        int numDoubleRooms = 0;
        double basePrice = 0.0;
//        java.util.ArrayList<Integer> allocatedRooms = new java.util.ArrayList<>(); //old

            int partySize = getNumberOfGuests();

            System.out.println("\n--- ROOM ALLOCATION OPTIONS ---");

            if (partySize == 1) {
                System.out.println("Option 1: 1 Single Room ($100)");
                System.out.println("Option 2: 1 Double Room ($150)");
                System.out.print("\nChoose your preferred option (1-2): ");
                int option = userIn.nextInt();
                userIn.nextLine(); // Clear buffer
                if (option == 1) {
                    numSingleRooms = 1;
                } else {
                    numDoubleRooms = 1;
                }
            } else if (partySize <= 9) {
                int maxDoubleRooms = (int) Math.ceil(partySize / 2.0);
                for (int i = 0; i <= maxDoubleRooms; i++) {
                    int singleRooms = partySize - (i * 2);
                    if (singleRooms >= 0) {
                        double optionPrice = (i * 150.0) + (singleRooms * 100.0);
                        System.out.println("Option " + (i + 1) + ": " +
                                (i > 0 ? i + " Double Room(s)" : "") +
                                (i > 0 && singleRooms > 0 ? " and " : "") +
                                (singleRooms > 0 ? singleRooms + " Single Room(s)" : "") +
                                " ($" + optionPrice + ")");
                    }
                }
                System.out.print("\nChoose your preferred option (1-" + (maxDoubleRooms + 1) + "): ");
                int option = userIn.nextInt();
                userIn.nextLine();
                numDoubleRooms = option - 1;
                numSingleRooms = partySize - (numDoubleRooms * 2);
                if (numSingleRooms < 0) {
                    System.out.println("Invalid option. Selecting optimal configuration...");
                    numDoubleRooms = partySize / 2;
                    numSingleRooms = partySize % 2;
                }
            }
        //else {
//            System.out.println("For parties larger than 9, please contact our group reservations desk.");
//            return;
//        }

        boolean canAccommodate = true;
        for (int i = 0; i < numSingleRooms; i++) {
            if (!checkAvailability(false)) {  //checkAvailability returns true when there is room avaliable here checking if there is a single room not avaliable aka all booked
                canAccommodate = false; //can't take more so set it to false
                System.out.println("Sorry, we don't have enough single rooms available.");
                break;
            }
        }
        if (canAccommodate) { //this means we weren't checking for single rooms in the first place
            for (int i = 0; i < numDoubleRooms; i++) {
                if (!checkAvailability(true)) {
                    canAccommodate = false;
                    System.out.println("Sorry, we don't have enough double rooms available."); //if double rooms were also full set flag into false and display this
                    break;
                }
            }
        }

        if (!canAccommodate) { //will trigger either double or single were full and you tried to book this type
            System.out.println("This room type is currently full please restart the booking process and choose another type of room/s");
            return;
        }

        // Calculate base price [If nothing from above got triggered]
        basePrice = (numSingleRooms * 100.0) + (numDoubleRooms * 150.0);
        // Add extra charges: $35 per room per night
        double extraCharges = (numSingleRooms + numDoubleRooms) * 35 * nightsStayed;
        double discount = hasDiscount() ? 25.0 : 0.0; // if has a discount is true get 25 off if false set discount value to 0
        this.totalPrice = basePrice + extraCharges - discount;

        System.out.println("\n--- BOOKING SUMMARY ---");
        System.out.println("Room Configuration: " +
                (numDoubleRooms > 0 ? numDoubleRooms + " Double Room(s)" : "") + //if numDoubleRooms you chose greater than 0 it will be triggered before the upcoming
                (numDoubleRooms > 0 && numSingleRooms > 0 ? " and " : "") + //this
                (numSingleRooms > 0 ? numSingleRooms + " Single Room(s)" : "")); //and this is the last if you only chose singles
        System.out.println("Total Guests: " + partySize);
        System.out.println("Base Price: $" + basePrice);
        System.out.println("Extra Charges ($35/night x " + nightsStayed + " nights x " + (numSingleRooms + numDoubleRooms) + " rooms): $" + extraCharges);
        if (hasDiscount()) {
            System.out.println("Discount Applied: $" + discount);
        }
        System.out.println("Total Price: $" + this.totalPrice);

        System.out.print("\nWould you like to confirm your booking? (y/n): ");
        String confirm = userIn.nextLine().trim().toLowerCase();
        if (confirm.equals("y")) {
            bookedRooms.clear();
            roomTypes.clear(); //clear them to handle the new bookings

            for (int i = 0; i < numSingleRooms; i++) { //indexing is num -1
                int roomNum = getAvailableRoom(false); // depending on the numSinglerooms you chose the loop will count and them
                //my logic is always faslse means single room //so isDouble false

                    bookedRooms.add(roomNum);
                    roomTypes.put(roomNum, false); // and here false

            }

            for (int i = 0; i < numDoubleRooms; i++) {
                int roomNum = getAvailableRoom(true); //Double room is true

                    bookedRooms.add(roomNum);
                    roomTypes.put(roomNum, true); //double room

            }

            if (!bookedRooms.isEmpty()) {
                roomNumber = bookedRooms.get(0);
            }
            hasBooked = true;

            System.out.println("Booking confirmed!");
            System.out.println("Your room assignments:");
            for (Integer room : bookedRooms) {
                boolean isDouble = roomTypes.get(room);
                System.out.println("- Room " + room + " (" + (isDouble ? "Double" : "Single") + ")");
            }
        } else {
            System.out.println("Booking cancelled.");
        }
        return;
    }

    public boolean isBooked() {
        return hasBooked;
    }

    public void displayBookingDetails() {
        if (!hasBooked) {
            System.out.println("No active booking to display.");
            return;
        }
        System.out.println("\n--- YOUR BOOKING DETAILS ---");
        if (bookedRooms.isEmpty()) {
            System.out.println("Room Number: " + roomNumber);
        } else {
            System.out.println("Room Assignments:");
            for (Integer room : bookedRooms) {
                Boolean isDouble = roomTypes.get(room);
                String roomType = (isDouble != null && isDouble) ? "Double" : "Single";
                System.out.println("- Room " + room + " (" + roomType + ")");
            }
        }

        int singleCount = 0;
        int doubleCount = 0;
        for (Boolean isDouble : roomTypes.values()) {
            if (isDouble != null && isDouble) {
                doubleCount++;
            } else {
                singleCount++;
            }
        }

        System.out.println("\nRoom Configuration: " +
                (doubleCount > 0 ? doubleCount + " Double Room(s)" : "") +
                (doubleCount > 0 && singleCount > 0 ? " and " : "") +
                (singleCount > 0 ? singleCount + " Single Room(s)" : ""));
        System.out.println("Number of Guests: " + getNumberOfGuests());
        System.out.println("Nights Stayed: " + nightsStayed);
        System.out.println("Total Price: $" + totalPrice);
    }

    /**
     * NEW METHOD:
     * Allows user to check out from a specific room.
     * Validates input and frees only that room.
     */
    public void checkOutSpecificRoom() {
        if (!hasBooked) {
            System.out.println("No active booking to check out from.");
            return;
        }

        displayBookingDetails(); // Show current booked rooms

        System.out.print("Enter the room number you want to check out from: ");
        int roomToCheckOut = userIn.nextInt();
        userIn.nextLine(); // Clear buffer

        // Call freeRoom method from Rooms class
        boolean success = ((Rooms)this).freeRoom(roomToCheckOut);

        if (success) {
            bookedRooms.remove(Integer.valueOf(roomToCheckOut));
            roomTypes.remove(roomToCheckOut);

            if (bookedRooms.isEmpty()) {
                hasBooked = false;
                System.out.println("All rooms have been vacated. Your booking is now complete.");
            } else {
                System.out.println("You have successfully checked out from room " + roomToCheckOut);
                displayBookingDetails();
            }
        } else {
            System.out.println("Could not check out from room " + roomToCheckOut);
        }
    }

}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Booking booking = new Booking();
        System.out.println("Welcome to Grand Hotel!");
        System.out.println("================================");



        boolean exit = false;
        while (!exit) {
            System.out.println("\n--- HOTEL MANAGEMENT SYSTEM ---");
            System.out.println("1. Make a new booking");
            System.out.println("2. View booking details");
            System.out.println("3. View room status");
            System.out.println("4. Hotel information");
            System.out.println("5. Check Out From A Room"); // NEW OPTION
            System.out.println("6. Exit");
            System.out.print("Choose an option (1-6): ");

            int choice = 0;
            try {
                choice = scanner.nextInt();
                scanner.nextLine(); // Clear buffer
            } catch (Exception e) {
                scanner.nextLine();
                System.out.println("Invalid input. Please enter a number.");
                continue;
            }

            switch (choice) {
                case 1:
                    //SetOnAction on this method
                    booking.book();
                    break;
                case 2:
                    //SetOnAction on this method
                    booking.displayBookingDetails();
                    break;
                case 3:
                    //SetOnAction on this method
                    booking.displayRoomStatus();
                    break;
                case 4:
                    //SetOnAction on this method
                    Hotel.displayInfo();
                    break;
                case 5:
                    //SetOnAction
                    booking.checkOutSpecificRoom();
                    break;
                case 6:

                    exit = true;
                    //SetOnAction
                    Main.exitText();

                    break;
                default:
                    System.out.println("Invalid option. Please try again.");
            }
        }

        scanner.close();
    }
    //تسهيلا بس علشان اللي هيعمل UI
public static void exitText(){
    System.out.println("Thank you for visiting Grand Hotel. Goodbye!");
}
}