package storage

import (
	"github.com/rs/zerolog/log"
	"gorm.io/gorm"

	"git.mstar.dev/mstar/linstrom/shared"
)

type RemoteServer struct {
	gorm.Model
	ServerType RemoteServerType // What software the server is running. Useful for formatting
	Domain     string           // `gorm:"primaryKey"` // Domain the server exists under. Additional primary key
	Name       string           // What the server wants to be known as (usually same as url)
	Icon       string           // ID of a media file
	IsSelf     bool             // Whether this server is yours truly
}

func (s *Storage) FindRemoteServerByDomain(url string) (*RemoteServer, error) {
	defer shared.Untrace(shared.Trace(&log.Logger))
	server := RemoteServer{}
	err := s.db.Where("domain = ?").First(&server).Error
	switch err {
	case nil:
		return &server, nil
	case gorm.ErrRecordNotFound:
		return nil, ErrEntryNotFound
	default:
		return nil, err
	}
}

// Find a remote server with a given display name
func (s *Storage) FindRemoteServerByDisplayName(displayName string) (*RemoteServer, error) {
	defer shared.Untrace(shared.Trace(&log.Logger))
	server := RemoteServer{}
	err := s.db.Where("name = ?", displayName).First(&server).Error
	switch err {
	case nil:
		return &server, nil
	case gorm.ErrRecordNotFound:
		return nil, ErrEntryNotFound
	default:
		return nil, err
	}
}

func (s *Storage) FindRemoteServerById(id uint) (*RemoteServer, error) {
	defer shared.Untrace(shared.Trace(&log.Logger))
	server := RemoteServer{}
	err := s.db.First(&server, id).Error
	switch err {
	case nil:
		return &server, nil
	case gorm.ErrRecordNotFound:
		return nil, ErrEntryNotFound
	default:
		return nil, err
	}
}

// Create a new remote server
func (s *Storage) NewRemoteServer(
	url, displayName, icon string,
	serverType RemoteServerType,
) (*RemoteServer, error) {
	defer shared.Untrace(shared.Trace(&log.Logger))
	_, err := s.FindRemoteServerByDomain(url)
	switch err {
	case nil:
		return nil, ErrEntryAlreadyExists
	case ErrEntryNotFound: // Empty case, not found is what we want
	default:
		return nil, err
	}
	server := RemoteServer{
		Domain:     url,
		Name:       displayName,
		Icon:       icon,
		ServerType: serverType,
	}
	err = s.db.Create(&server).Error
	if err != nil {
		return nil, err
	}
	return &server, nil
}

// Update a remote server with the given url
// If displayName is set, update that
// If icon is set, update that
// Returns the updated version
func (s *Storage) UpdateRemoteServer(url string, displayName, icon *string) (*RemoteServer, error) {
	defer shared.Untrace(shared.Trace(&log.Logger))
	if displayName == nil && icon == nil {
		return nil, ErrNothingToChange
	}
	server, err := s.FindRemoteServerByDomain(url)
	if err != nil {
		return nil, err
	}
	if displayName != nil {
		server.Name = *displayName
	}
	if icon != nil {
		server.Icon = *icon
	}
	err = s.db.Save(server).Error
	if err != nil {
		return nil, err
	}
	return server, nil
}
