package channelserver

import (
	"database/sql"
	"encoding/binary"

	"erupe-ce/network/mhfpacket"
	"erupe-ce/server/channelserver/compression/nullcomp"
	"go.uber.org/zap"
)

const (
	CharacterSaveRPPointer = 0x22D16
)

type CharacterSaveData struct {
	CharID         uint32
	Name           string
	RP             uint16
	IsNewCharacter bool

	// Use provided setter/getter
	baseSaveData []byte
}

func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
	result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)

	if err != nil {
		s.logger.Error("failed to retrieve save data for character", zap.Error(err), zap.Uint32("charID", charID))
		return nil, err
	}

	defer result.Close()

	saveData := &CharacterSaveData{}
	var compressedBaseSave []byte

	if !result.Next() {
		s.logger.Error("no results found for character save data", zap.Uint32("charID", charID))
		return nil, err
	}

	err = result.Scan(&saveData.CharID, &compressedBaseSave, &saveData.IsNewCharacter, &saveData.Name)

	if err != nil {
		s.logger.Error(
			"failed to retrieve save data for character",
			zap.Error(err),
			zap.Uint32("charID", charID),
		)

		return nil, err
	}

	if compressedBaseSave == nil {
		return saveData, nil
	}

	decompressedBaseSave, err := nullcomp.Decompress(compressedBaseSave)

	if err != nil {
		s.logger.Error("Failed to decompress savedata from db", zap.Error(err))
		return nil, err
	}

	saveData.SetBaseSaveData(decompressedBaseSave)

	return saveData, nil
}

func (save *CharacterSaveData) Save(s *Session, transaction *sql.Tx) error {
	// We need to update the save data byte array before we save it back to the DB
	save.updateSaveDataWithStruct()

	compressedData, err := save.CompressedBaseData(s)

	if err != nil {
		return err
	}

	updateSQL := "UPDATE characters	SET savedata=$1, is_new_character=$3 WHERE id=$2"

	if transaction != nil {
		_, err = transaction.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
	} else {
		_, err = s.server.db.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
	}
	if err != nil {
		s.logger.Error("failed to save character data", zap.Error(err), zap.Uint32("charID", save.CharID))
		return err
	}
	return nil
}

func (save *CharacterSaveData) CompressedBaseData(s *Session) ([]byte, error) {
	compressedData, err := nullcomp.Compress(save.baseSaveData)

	if err != nil {
		s.logger.Error("failed to compress saveData", zap.Error(err), zap.Uint32("charID", save.CharID))
		return nil, err
	}
	return compressedData, nil
}

func (save *CharacterSaveData) BaseSaveData() []byte {
	return save.baseSaveData
}

func (save *CharacterSaveData) SetBaseSaveData(data []byte) {
	save.baseSaveData = data
	// After setting the new save byte array, we can extract the values to update our struct
	// This will be useful when we save it back, we use the struct values to overwrite the saveData
	save.updateStructWithSaveData()
}

// This will update the save struct with the values stored in the raw savedata arrays
func (save *CharacterSaveData) updateSaveDataWithStruct() {
	rpBytes := make([]byte, 2)
	binary.LittleEndian.PutUint16(rpBytes, save.RP)
	copy(save.baseSaveData[CharacterSaveRPPointer:CharacterSaveRPPointer+2], rpBytes)
}

// This will update the character save struct with the values stored in the raw savedata arrays
func (save *CharacterSaveData) updateStructWithSaveData() {
	save.RP = binary.LittleEndian.Uint16(save.baseSaveData[CharacterSaveRPPointer : CharacterSaveRPPointer+2])
}

func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
	pkt := p.(*mhfpacket.MsgMhfSexChanger)
	doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
