package v1

import (
	"cwtch.im/cwtch/event"
	"cwtch.im/cwtch/model"
	"encoding/json"
	"git.openprivacy.ca/openprivacy/log"
	"os"
	"path"
)

const profileFilename = "profile"
const saltFile = "SALT"

// ProfileStoreV1 storage for profiles and message streams that uses in memory key and fs stored salt instead of in memory password
type ProfileStoreV1 struct {
	fs        FileStore
	directory string
	profile   *model.Profile
	key       [32]byte
	salt      [128]byte
}

// LoadProfileWriterStore loads a profile store from filestore listening for events and saving them
// directory should be $appDir/profiles/$rand
func LoadProfileWriterStore(directory, password string) (*ProfileStoreV1, error) {
	salt, err := os.ReadFile(path.Join(directory, saltFile))
	if err != nil {
		return nil, err
	}

	key := CreateKey(password, salt)

	ps := &ProfileStoreV1{fs: NewFileStore(directory, profileFilename, key), key: key, directory: directory, profile: nil}
	copy(ps.salt[:], salt)

	err = ps.load()
	if err != nil {
		return nil, err
	}

	return ps, nil
}

// load instantiates a cwtchPeer from the file store
func (ps *ProfileStoreV1) load() error {
	decrypted, err := ps.fs.Read()
	if err != nil {
		return err
	}
	cp := new(model.Profile)
	err = json.Unmarshal(decrypted, &cp)

	if err == nil {
		ps.profile = cp

		// TODO 2020.06.09: v1 update, Remove on v2
		// if we already have the contact it can be assumed "approved" unless blocked
		for _, contact := range cp.Contacts {
			if contact.Authorization == "" {
				if contact.DeprecatedBlocked {
					contact.Authorization = model.AuthBlocked
				} else {
					contact.Authorization = model.AuthApproved
				}
			}

			if contact.Attributes[event.SaveHistoryKey] == event.SaveHistoryConfirmed {
				ss := NewStreamStore(ps.directory, contact.LocalID, ps.key)
				if contact, exists := cp.Contacts[contact.Onion]; exists {
					contact.Timeline.SetMessages(ss.Read())
				}
			}
		}

		for gid, group := range cp.Groups {
			if group.Version == 0 {
				log.Debugf("group %v is of unsupported version 0. dropping group...\n", group.GroupID)
				delete(cp.Groups, gid)
				continue
			}
			ss := NewStreamStore(ps.directory, group.LocalID, ps.key)
			if group, exists := cp.Groups[gid]; exists {
				group.Timeline.SetMessages(ss.Read())
				group.Timeline.Sort()
			}
		}
	}

	return err
}

// GetProfileCopy returns a copy of the stored profile
func (ps *ProfileStoreV1) GetProfileCopy(timeline bool) *model.Profile {
	return ps.profile.GetCopy(timeline)
}
