package main

import (
	"path/filepath"

	"github.com/asdine/storm/v3"
)

// Location is a top level component that starts the containerization process
type Location struct {
	ID            int    `storm:"id,increment,index"`
	Name          string `storm:"unique"`
	Description   string
	CoverPhoto    string   // A "cover photo" for the location
	Photos        []string // A list of additional photos for the location
	Rooms         []Room
	Notes         string
	Address       string
	SquareFeet    int
	Latitude      string
	Longitude     string
	DatePurchased string
	PurchasePrice string
	CurrentValue  string
}

// Room is a containerized area at a location
type Room struct {
	ID          int    `storm:"id,increment,index"`
	Name        string `storm:"unique"`
	Description string
	CoverPhoto  string   // A "cover photo" for the room
	Photos      []string // A list of additional photos for the room
	Notes       string
	Cabinets    []Cabinet
	Items       []Item
	SquareFeet  int

	LocationID   int    //Which location room is assigned to
	LocationName string // Location name room belongs to

}

// Cabinet is a containerized area in a room
type Cabinet struct {
	ID         int    `storm:"id, increment"`
	RoomID     int    // Room ID that the cabinet is assigned to
	CoverPhoto string // A "Cover photo" for this item
	Name       string
	Notes      string
}

// Item is contained inside a room or cabinet
type Item struct {
	ID         int      `storm:"id, increment"`
	RoomID     int      // Room ID that item is assigned to
	CoverPhoto string   // A "cover photo" for the item
	Photos     []string // A list of additional photos for the item
	Count      int      // If item has duplicates
	Name       string
	Product    string
	Serial     string
	Warranty   string
	Notes      string
}

//SetupDatabase initializes the storm/bbolt database
func SetupDatabase(s *Server) (db *storm.DB) {
	db, err := storm.Open(filepath.Clean("./app/database/goInventorize.db"))
	if err != nil {
		s.Log.Fatal().Msgf("Unable to create/open database! %s", err)
	}
	return db
}

// AddLocation adds a location to the database
func (s *Server) AddLocation(loc Location) error {
	s.Log.Info().Msgf("Adding new location to database: ", loc)
	err := s.Database.Save(&loc)
	if err != nil {
		s.Log.Error().Msgf("Unable to add new location to database: %s error: %s ", loc, err)
		return err
	}
	return nil
}

// DeleteLocation deletes a location by ID
func (s *Server) DeleteLocation(loc Location) error {
	s.Log.Info().Msgf("Deleting location from database: ", loc)
	err := s.Database.DeleteStruct(&loc)
	if err != nil {
		s.Log.Error().Msgf("Unable to delete location from database: %s error: %s ", loc, err)
		return err
	}
	return nil
}

// GetLocations fetches all of the locations from the database
func (s *Server) GetLocations() (locations []Location, err error) {
	err = s.Database.All(&locations)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all locations: %s", err)
		return nil, err
	}
	return locations, nil
}

// GetLocation fetches a single location
func (s *Server) GetLocation(locID int) (location Location, err error) {
	err = s.Database.One("ID", locID, &location)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all rooms at location: %s", err)
		return location, err
	}
	return location, nil
}

// AddRoom adds a room to a location
func (s *Server) AddRoom(room Room) error {
	s.Log.Info().Msgf("Adding new room to location: %s", room)
	err := s.Database.Save(&room)
	if err != nil {
		s.Log.Error().Msgf("Unable to add new room to database: %s error: %s", room, err)
		return err
	}
	return nil
}

// DeleteRoom deletes a room
func (s *Server) DeleteRoom(room Room) error {
	s.Log.Info().Msgf("Deleting room from database: %s", room)
	err := s.Database.DeleteStruct(&room)
	if err != nil {
		s.Log.Error().Msgf("Unable to delete location from database: %s error: %s ", room, err)
		return err
	}
	return nil
}

// GetAllRooms gets all of the rooms
func (s *Server) GetAllRooms() (rooms []Room, err error) {
	s.Log.Info().Msg("Getting all Rooms")
	err = s.Database.All(&rooms)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all room with error: %s", err)
		return rooms, err
	}
	return rooms, nil
}

// GetRoom gets a room based on id
func (s *Server) GetRoom(roomID int) (room Room, err error) {
	s.Log.Info().Msgf("Fetching room with id: %d", roomID)
	err = s.Database.One("ID", roomID, &room)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch room with id: %d with error: %s", roomID, err)
		return room, err
	}
	return room, nil
}

// AddItem adds an item to a room
func (s *Server) AddItem(item Item) error {
	s.Log.Info().Msgf("Adding new item to room: ", item)
	err := s.Database.Save(&item)
	if err != nil {
		s.Log.Error().Msgf("Unable to add new item to room: %s error: %s", item, err)
		return err
	}
	return nil
}

// GetRoomsAtLocation fetches all the rooms assigned to a location by location ID
func (s *Server) GetRoomsAtLocation(locID int) (rooms []Room, err error) {
	err = s.Database.Find("LocationID", locID, &rooms)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all rooms at location: %s", err)
		return nil, err
	}
	return rooms, nil
}

// GetCabinetsInRoom fetches all of the cabinets assigned to a room
func (s *Server) GetCabinetsInRoom(roomID int) (cabinets []Cabinet, err error) {
	err = s.Database.Find("Room", roomID, &cabinets)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all cabinets in room: %s", err)
		return nil, err
	}
	return cabinets, nil
}

// GetItemsInRoom fetches all of the items assigned to a room
func (s *Server) GetItemsInRoom(roomID int) (items []Item, err error) {
	err = s.Database.Find("Room", roomID, &items)
	if err != nil {
		s.Log.Error().Msgf("Unable to fetch all items in room: %s", err)
		return nil, err
	}
	return items, nil
}
