/**
 * license: GPLv3
 * lodge.reservationsystem
*/

package lodge;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

import lodge.data.Account;
import lodge.data.Address;
import lodge.data.DuplicateObjectException;
import lodge.data.EmailAddress;
import lodge.data.ReservationStatusEnum;
import lodge.reservation.IReservation;
import lodge.reservation.Reservation;
import lodge.reservationsystem.AccomodationManager;
import lodge.reservationsystem.CabinReservation;
import lodge.reservationsystem.HotelReservation;
import lodge.reservationsystem.HouseReservation;

/**
 * The Tests for the ReservationSystem Module
 *
 * <p>
 * This class main function acts as a driver function to run the test functions.
 * </p>
 *
 * @author Sherwin Price
 * @version 1.0
 * @since 2025
 */
public final class TestReservations {
        // Request that Manager updates specific account’s files with data stored in
        static void Test_AddAccount(AccomodationManager mgr, Account acct) throws Exception {
                mgr.AddAccount(acct);

                // 4. Request that Manager updates specific account’s files with data stored in
                // memory
                mgr.UpdateAccount(acct);
        }

        static void Test_AddReservation(AccomodationManager mgr, Account acct, Reservation rsrv, Address mlAddr,
                        ZonedDateTime zdtStrt)
                        throws Exception {

                rsrv.setMailing_address(mlAddr);
                rsrv.setNumberOfBeds(2);
                rsrv.setNumberOfFloors(1);
                rsrv.setNumberOfBedRooms(1);
                rsrv.setSquareFeet(450);
                rsrv.setReservation_start_date(zdtStrt);
                rsrv.setReservation_end_date(zdtStrt.plus(4, ChronoUnit.DAYS));

                mgr.addReservation(acct, rsrv);

                mgr.UpdateAccount(acct);
        }

        public static void main(String[] args) throws Exception {

                // Configure data repository
                AccomodationManager mgr = new AccomodationManager(getRepositoryConfig.getPath());

                // 1. Get the list of loaded accounts from Manager
                mgr.loadAll();

                // 2. Retrieve a loaded account object that matches a specific account number
                mgr.retrieveAccount("A1450981765");

                // 3. Add new account object to the list managed by Manager (if account object
                // already exists on add action with the same account number, it is considered
                // an error)

                Test_AddAccount(mgr, mgr.newAccount("701-456-7890",
                                new Address("10 wilco ave", "wilco", "WY", "82801"),
                                new EmailAddress("wilco@wyommin.net")));

                Test_AddAccount(mgr, mgr.newAccount("301-356-3890",
                                new Address("30 Amstadam ave", "New York", "NY", "12010"),
                                new EmailAddress("newbee952@aol.com")));

                // 5. Add draft lodging reservation to an account (if reservation object already
                // exists with the same reservation number, it is considered an error)
                Account acct2 = mgr.retrieveLoadedAccounts().get(1);
                Test_AddReservation(mgr, acct2,
                                new HotelReservation(
                                                new Address("400 hotel ave", "Maryland City", "MD", "20723")),
                                new Address("400 hotel ave", "Maryland City", "MD", "20723"),
                                ZonedDateTime.of(2025, 9, 05, 10, 0, 0, 0, ZoneId.of("UTC")));

                Account acct1 = mgr.retrieveLoadedAccounts().get(0);
                Test_AddReservation(mgr, acct2,
                                new CabinReservation(new Address("30 cabin ave", "Carnelian", "CA", "96140")),
                                new Address("30 cabin ave", "Carnelian Bay", "CA", "96140"),
                                ZonedDateTime.of(2025, 9, 05, 10, 0, 0, 0, ZoneId.of("UTC")));

                Test_AddReservation(mgr, acct1,
                                new CabinReservation(new Address("40 cabin ave", "Carnelian", "CA", "96140")),
                                new Address("40 cabin ave", "Carnelian Bay", "CA", "96140"),
                                ZonedDateTime.of(2025, 9, 05, 10, 0, 0, 0, ZoneId.of("UTC")));

                Test_AddReservation(mgr, acct1,
                                new HouseReservation(new Address("3000 Osage ave", "GreenBelt", "MD", "20740")),
                                new Address("40012 College ave", "College Park", "MD", "20740"),
                                ZonedDateTime.of(2025, 9, 05, 10, 0, 0, 0, ZoneId.of("UTC")));

                try {
                        IReservation irsrv = acct1.getAllReservations().next();
                        if (mgr.addReservation(acct1, acct1.findReservation(irsrv.getReservation_number()))) {
                                mgr.UpdateAccount(mgr.retrieveAccount(irsrv.getAccountNumber()));
                        }
                } catch (DuplicateObjectException e) {
                        System.out.println(e.getMessage());
                }

                Account account = mgr.retrieveLoadedAccounts().get(0);

                // 6. Complete reservation that is associated with an account
                IReservation irsrv = account.getAllReservations().next();
                Reservation rsrv = account.findReservation(irsrv.getReservation_number());
                rsrv.Change(rsrv, ReservationStatusEnum.Completed);
                mgr.UpdateAccount(mgr.retrieveAccount(account.getAccount_number()));

                // 7. Cancel reservation that is associated with an account
                account = mgr.retrieveLoadedAccounts().getLast();
                IReservation ir = account.getAllReservations().next();
                rsrv = (Reservation) ir;
                rsrv.Change(rsrv, ReservationStatusEnum.Canceled);
                mgr.UpdateAccount(mgr.retrieveAccount(rsrv.getAccountNumber()));

                // 8. Change reservation values that can be changed (if reservation is
                // cancelled, completed, or for past date, it is considered an error)
                rsrv.Change(rsrv, ReservationStatusEnum.Completed);
                rsrv.update(rsrv);

                // 9. Request for price per night to be calculated and returned for a
                // per night
                // reservation

                // 10. Request for total reservation price to be calculated and returned for
                // specific reservation
                rsrv = account.findReservation(rsrv.getReservation_number());
                rsrv.calculatePrice();

                mgr.showReservationList();

                System.out.println("Program Completed.");
        }

        public final static class getRepositoryConfig {
                public final static String getPath() {
                        String home = System.getenv("HOME") != null ? System.getenv("HOME")
                                        : System.getenv("HOMEDRIVE") + System.getenv("HOMEPATH");
                        return home.replace('\\', '/') + "/workspace/reservationsystem/src/resources";
                }
        }
}
