package data

import (
	"archive/zip"
	"encoding/binary"
	"fmt"
	"io"
	"os"
	"reflect"
	"time"
)

type Action uint8

const (
	ActionNone  Action = iota
	ActionLong  Action = iota
	ActionBank  Action = iota
	ActionShort Action = iota
)

type Item struct {
	Date    int64
	Price   float64 // ценник валюты
	Balance float64 // баланс кошелька
	Action  Action  // действие по Trade
	Trade   float64 // Trade сделка произошла по этой цене
}

var itemSizeOf int64

var LastTrade Item

var dbName string

func NewItem(price float64, balance float64, action Action, trade float64) Item {
	return Item{
		Price:   price,
		Balance: balance,
		Action:  action,
		Trade:   trade,
	}
}

func Init(databaseName string) {

	dbName = databaseName

	itemSizeOf = sizeof(reflect.TypeOf(Item{}))

	db, err := os.OpenFile(dbName, os.O_CREATE, 0600)

	if err != nil {
		fmt.Println("Failed create database")
	}

	db.Close()

}

func Add(item Item) {
	db, err := os.OpenFile(dbName, os.O_RDWR|os.O_APPEND, 0600)

	if err != nil {
		fmt.Println("Failed open database")
	}

	item.Date = time.Now().UnixMilli()

	binary.Write(db, binary.LittleEndian, &item)

	db.Close()
}

func ZipPath() string {
	items := items(0)

	string := ""
	for _, item := range items {
		string += fmt.Sprint(item.Price) + "\n"
	}

	filename := "Prices_" + time.Now().Format("02.01.2006")

	filenameTxt := filename + ".txt"
	filenameZip := filename + ".zip"

	os.WriteFile(filenameTxt, []byte(string), 0644)

	archive, err := os.Create(filenameZip)
	if err != nil {
		fmt.Println(err)
	}
	defer archive.Close()

	zipWriter := zip.NewWriter(archive)

	f1, err := os.Open(filenameTxt)
	if err != nil {
		fmt.Println(err)
	}
	defer f1.Close()

	w1, err := zipWriter.Create(filenameTxt)
	if err != nil {
		fmt.Println(err)
	}
	if _, err := io.Copy(w1, f1); err != nil {
		fmt.Println(err)
	}

	zipWriter.Close()

	os.Remove(filenameTxt)

	return filenameZip
}

func DataLastYear() []Item {
	return items(60 * 60 * 24 * 365)
}

func DataLastMonth() []Item {
	return items(60 * 60 * 24 * 30)
}

func DataLastWeek() []Item {
	return items(60 * 60 * 24 * 7)
}

func DataLastDay() []Item {
	return items(60 * 60 * 24)
}

func items(length int64) []Item {
	db, err := os.OpenFile(dbName, os.O_RDONLY, 0600)

	if err != nil {
		fmt.Println("Failed open database")
	}

	var items []Item

	if length > 0 {
		db.Seek(-itemSizeOf*length, os.SEEK_END)
	}

	for i := 0; i < int(length); i++ {
		var item Item

		err := binary.Read(db, binary.LittleEndian, &item)

		if err == io.EOF {
			break
		}

		items = append(items, item)
	}

	db.Close()

	return items
}

func sizeof(t reflect.Type) int64 {
	switch t.Kind() {
	case reflect.Array:
		if s := sizeof(t.Elem()); s >= 0 {
			return s * int64(t.Len())
		}

	case reflect.Struct:
		sum := int64(0)
		for i, n := 0, t.NumField(); i < n; i++ {
			s := sizeof(t.Field(i).Type)
			if s < 0 {
				return -1
			}
			sum += s
		}
		return sum

	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
		reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
		reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		return int64(t.Size())

	case reflect.Slice:
		return sizeof(t.Elem())
	}

	return -1
}
