package storage

import (
	"crypto/tls"
	"os"
	"sync"
	"time"
)

type User struct {
	ID       uint64
	IDBytes  []byte
	Username string

	store          Store
	messageLog     MessageStore
	messageIndex   MessageSearchProvider
	clientSettings *ClientSettings
	lastIP         []byte
	certificate    *tls.Certificate
	lock           sync.Mutex
}

func NewUser(store Store) (*User, error) {
	user := &User{
		store:          store,
		clientSettings: DefaultClientSettings(),
	}

	err := store.SaveUser(user)
	if err != nil {
		return nil, err
	}

	err = os.MkdirAll(Path.User(user.Username), 0700)
	if err != nil {
		return nil, err
	}

	return user, nil
}

func LoadUsers(store Store) ([]*User, error) {
	users, err := store.GetUsers()
	if err != nil {
		return nil, err
	}

	for _, user := range users {
		user.store = store
		user.loadCertificate()
	}

	return users, nil
}

func (u *User) SetMessageStore(store MessageStore) {
	u.messageLog = store
}

func (u *User) SetMessageSearchProvider(search MessageSearchProvider) {
	u.messageIndex = search
}

func (u *User) Remove() {
	u.store.DeleteUser(u)
	if u.messageLog != nil {
		u.messageLog.Close()
	}
	if u.messageIndex != nil {
		u.messageIndex.Close()
	}
	os.RemoveAll(Path.User(u.Username))
}

func (u *User) GetLastIP() []byte {
	u.lock.Lock()
	ip := u.lastIP
	u.lock.Unlock()
	return ip
}

func (u *User) SetLastIP(ip []byte) error {
	u.lock.Lock()
	u.lastIP = ip
	u.lock.Unlock()

	return u.store.SaveUser(u)
}

//easyjson:json
type ClientSettings struct {
	ColoredNicks bool
}

func DefaultClientSettings() *ClientSettings {
	return &ClientSettings{
		ColoredNicks: true,
	}
}

func (u *User) GetClientSettings() *ClientSettings {
	u.lock.Lock()
	settings := *u.clientSettings
	u.lock.Unlock()
	return &settings
}

func (u *User) SetClientSettings(settings *ClientSettings) error {
	u.lock.Lock()
	u.clientSettings = settings
	u.lock.Unlock()

	return u.store.SaveUser(u)
}

func (u *User) UnmarshalClientSettingsJSON(b []byte) error {
	u.lock.Lock()
	err := u.clientSettings.UnmarshalJSON(b)
	u.lock.Unlock()

	if err != nil {
		return err
	}

	return u.store.SaveUser(u)
}

type Server struct {
	Name     string
	Host     string
	Port     string
	TLS      bool
	Password string
	Nick     string
	Username string
	Realname string
}

func (u *User) GetServer(address string) (*Server, error) {
	return u.store.GetServer(u, address)
}

func (u *User) GetServers() ([]*Server, error) {
	return u.store.GetServers(u)
}

func (u *User) AddServer(server *Server) error {
	return u.store.SaveServer(u, server)
}

func (u *User) RemoveServer(address string) error {
	return u.store.RemoveServer(u, address)
}

func (u *User) SetNick(nick, address string) error {
	server, err := u.GetServer(address)
	if err != nil {
		return err
	}
	server.Nick = nick
	return u.AddServer(server)
}

func (u *User) SetServerName(name, address string) error {
	server, err := u.GetServer(address)
	if err != nil {
		return err
	}
	server.Name = name
	return u.AddServer(server)
}

// TODO: Remove topic from disk schema
type Channel struct {
	Server string
	Name   string
	Topic  string
}

func (u *User) GetChannels() ([]*Channel, error) {
	return u.store.GetChannels(u)
}

func (u *User) AddChannel(channel *Channel) error {
	return u.store.AddChannel(u, channel)
}

func (u *User) RemoveChannel(server, channel string) error {
	return u.store.RemoveChannel(u, server, channel)
}

type Message struct {
	ID      string `json:"-" bleve:"-"`
	Server  string `json:"-" bleve:"server"`
	From    string `bleve:"-"`
	To      string `json:"-" bleve:"to"`
	Content string `bleve:"content"`
	Time    int64  `bleve:"-"`
}

func (m Message) Type() string {
	return "message"
}

func (u *User) LogMessage(id, server, from, to, content string) error {
	message := &Message{
		ID:      id,
		Server:  server,
		From:    from,
		To:      to,
		Content: content,
		Time:    time.Now().Unix(),
	}

	err := u.messageLog.LogMessage(message)
	if err != nil {
		return err
	}
	return u.messageIndex.Index(id, message)
}

func (u *User) GetMessages(server, channel string, count int, fromID string) ([]Message, bool, error) {
	return u.messageLog.GetMessages(server, channel, count, fromID)
}

func (u *User) GetLastMessages(server, channel string, count int) ([]Message, bool, error) {
	return u.GetMessages(server, channel, count, "")
}

func (u *User) SearchMessages(server, channel, q string) ([]Message, error) {
	ids, err := u.messageIndex.SearchMessages(server, channel, q)
	if err != nil {
		return nil, err
	}

	return u.messageLog.GetMessagesByID(server, channel, ids)
}
