package script

import (
	"encoding/json"
	"fmt"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

var serverSecret string
var serverConfig Config
var serverCurrencies []LedgerCurrency
var ledgerConfigMap map[string]Config
var ledgerAccountsMap map[string][]Account
var ledgerAccountTypesMap map[string]map[string]string
var ledgerCurrencyMap map[string][]LedgerCurrency
var whiteList []string

type Config struct {
	Id                string `json:"id,omitempty"`
	Mail              string `json:"mail,omitempty"`
	Title             string `json:"title,omitempty"`
	DataPath          string `json:"dataPath,omitempty"`
	OperatingCurrency string `json:"operatingCurrency"`
	StartDate         string `json:"startDate"`
	IsBak             bool   `json:"isBak"`
	OpeningBalances   string `json:"openingBalances"`
	CreateDate        string `json:"createDate,omitempty"`
}

type Account struct {
	Acc       string `json:"account"`
	StartDate string `json:"startDate"`
	Currency  string `json:"currency,omitempty"` // 货币
	//CurrencySymbol       string            `json:"currencySymbol,omitempty"`    // 货币符号
	Currencies []AccountCurrency `json:"currencies,omitempty"` // 多个货币单位
	//Price                string            `json:"price,omitempty"`             // 汇率
	//PriceDate            string            `json:"priceDate,omitempty"`         // 汇率日期
	//IsAnotherCurrency    bool              `json:"isAnotherCurrency,omitempty"` // 其他币种标识
	IsCurrent            bool              `json:"isCurrent,omitempty"`
	Positions            []AccountPosition `json:"positions,omitempty"`
	MarketNumber         string            `json:"marketNumber,omitempty"`
	MarketCurrency       string            `json:"marketCurrency,omitempty"`
	MarketCurrencySymbol string            `json:"marketCurrencySymbol,omitempty"`
	EndDate              string            `json:"endDate,omitempty"`
	Type                 *AccountType      `json:"type,omitempty"`
	Status               bool              `json:"status,omitempty"`
}

type AccountCurrency struct {
	Currency          string `json:"currency,omitempty"`          // 货币
	CurrencySymbol    string `json:"currencySymbol,omitempty"`    // 货币符号
	IsAnotherCurrency bool   `json:"isAnotherCurrency,omitempty"` // 其他币种标识
	Price             string `json:"price,omitempty"`             // 汇率
	PriceDate         string `json:"priceDate,omitempty"`         // 汇率日期
}

type AccountPosition struct {
	Number         string `json:"number,omitempty"`
	Currency       string `json:"currency,omitempty"`
	CurrencySymbol string `json:"currencySymbol,omitempty"`
}

type AccountType struct {
	Key  string `json:"key"`
	Name string `json:"name"`
}

type LedgerCurrency struct {
	Name      string `json:"name"`
	Currency  string `json:"currency"`
	Symbol    string `json:"symbol"`
	Current   bool   `json:"current,omitempty"`
	Price     string `json:"price,omitempty"`
	PriceDate string `json:"priceDate,omitempty"`
}

func GetServerConfig() Config {
	return serverConfig
}

func LoadServerConfig() error {
	filePath := GetServerConfigFilePath()
	if !FileIfExist(filePath) {
		serverConfig = Config{
			OpeningBalances:   "Equity:OpeningBalances",
			OperatingCurrency: "CNY",
			StartDate:         "1970-01-01",
			IsBak:             true,
		}
		return nil
	}
	fileContent, err := ReadFile(filePath)
	if err != nil {
		return err
	}
	err = json.Unmarshal(fileContent, &serverConfig)
	if err != nil {
		LogSystemError("Failed unmarshall config file (" + filePath + ")")
		return err
	}
	LogSystemInfo("Success load config file (" + filePath + ")")
	// load white list
	whiteListFilePath := GetServerWhiteListFilePath()
	if FileIfExist(whiteListFilePath) {
		fileContent, err = ReadFile(whiteListFilePath)
		if err != nil {
			return err
		}
		err = json.Unmarshal(fileContent, &whiteList)
		if err != nil {
			LogSystemError("Failed unmarshal whitelist file (" + whiteListFilePath + ")")
			return err
		}
	} else {
		err = CreateFile(whiteListFilePath)
		if err != nil {
			return err
		}
		err = WriteFile(whiteListFilePath, "[]")
		if err != nil {
			return err
		}
		whiteList = make([]string, 0)
	}
	LogSystemInfo("Success load whitelist file (" + whiteListFilePath + ")")
	return nil
}

func UpdateServerConfig(config Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}
	err = WriteFile(GetServerConfigFilePath(), string(bytes))
	if err != nil {
		return err
	}
	serverConfig = config
	return nil
}

func GetLedgerConfigMap() map[string]Config {
	return ledgerConfigMap
}

func GetLedgerConfig(ledgerId string) *Config {
	for k, v := range ledgerConfigMap {
		if k == ledgerId {
			return &v
		}
	}
	return nil
}

func GetLedgerConfigByMail(mail string) *Config {
	for _, v := range ledgerConfigMap {
		if v.Mail == mail {
			return &v
		}
	}
	return nil
}

func GetLedgerConfigFromContext(c *gin.Context) *Config {
	ledgerConfig, _ := c.Get("LedgerConfig")
	t, _ := ledgerConfig.(*Config)
	return t
}

func GetLedgerAccounts(ledgerId string) []Account {
	return ledgerAccountsMap[ledgerId]
}

func GetLedgerAccount(ledgerId string, account string) Account {
	accounts := ledgerAccountsMap[ledgerId]
	for _, acc := range accounts {
		if acc.Acc == account {
			return acc
		}
	}
	panic("Invalid account")
}

func UpdateLedgerAccounts(ledgerId string, accounts []Account) {
	ledgerAccountsMap[ledgerId] = accounts
}

func ClearLedgerAccounts(ledgerId string) {
	delete(ledgerAccountsMap, ledgerId)
}

func GetLedgerAccountTypes(ledgerId string) map[string]string {
	return ledgerAccountTypesMap[ledgerId]
}

func UpdateLedgerAccountTypes(ledgerId string, accountTypesMap map[string]string) {
	ledgerAccountTypesMap[ledgerId] = accountTypesMap
}

func ClearLedgerAccountTypes(ledgerId string) {
	delete(ledgerAccountTypesMap, ledgerId)
}

func GetAccountType(ledgerId string, acc string) AccountType {
	accountTypes := ledgerAccountTypesMap[ledgerId]
	accNodes := strings.Split(acc, ":")
	accountType := AccountType{
		Key: acc,
		// 默认取最后一个节点
		Name: accNodes[len(accNodes)-1],
	}
	var matchKey = ""
	for key, name := range accountTypes {
		if strings.Contains(acc, key) && len(matchKey) < len(key) {
			matchKey = key
			accountType = AccountType{Key: key, Name: name}
		}
	}
	return accountType
}

func IsInWhiteList(ledgerId string) bool {
	// ledger white list is empty, return true
	if len(whiteList) == 0 {
		return true
	}
	for i := range whiteList {
		if whiteList[i] == ledgerId {
			return true
		}
	}
	return false
}

func LoadLedgerConfigMap() error {
	path := GetServerLedgerConfigFilePath()
	// 文件不存在，则创建 ledger_config.json
	if !FileIfExist(path) {
		err := CreateFile(path)
		if err != nil {
			return err
		}
		ledgerConfigMap = make(map[string]Config)
	} else {
		// 文件存在，将文件内容加载到缓存
		fileContent, err := ReadFile(path)
		if err != nil {
			return err
		}
		if string(fileContent) != "" {
			err = json.Unmarshal(fileContent, &ledgerConfigMap)
			if err != nil {
				LogSystemError("Failed unmarshal ledger_config file (" + path + ")")
				return err
			}
		} else {
			ledgerConfigMap = make(map[string]Config)
		}
		LogSystemInfo("Success load ledger_config file (" + path + ")")
	}
	return nil
}

func LoadLedgerAccountsMap() error {
	if ledgerAccountsMap == nil {
		ledgerAccountsMap = make(map[string][]Account)
	}
	for _, config := range ledgerConfigMap {
		// 兼容性处理
		err := handleCompatible(config)
		if err != nil {
			return err
		}
		err = LoadLedgerAccounts(config.Id)
		if err != nil {
			return err
		}
		err = LoadLedgerCurrencyMap(&config)
		if err != nil {
			return err
		}
	}
	return nil
}

func handleCompatible(config Config) error {
	// 兼容性处理，.beancount-ns -> .beancount-gs
	beancountGsConfigPath := GetLedgerConfigDocument(config.DataPath)
	beancountNsConfigPath := GetCompatibleLedgerConfigDocument(config.DataPath)
	if FileIfExist(beancountNsConfigPath) && !FileIfExist(beancountGsConfigPath) {
		err := CopyDir(beancountNsConfigPath, beancountGsConfigPath)
		if err != nil {
			return err
		}
	}
	return nil
}

func LoadLedgerAccounts(ledgerId string) error {
	config := ledgerConfigMap[ledgerId]
	// 加载 account_type.json 到缓存（内存）
	loadErr := LoadLedgerAccountTypesMap(config)
	if loadErr != nil {
		LogSystemError("Failed to load account types")
		return loadErr
	}
	accountDirPath := config.DataPath + "/account"
	dirs, err := os.ReadDir(accountDirPath)
	if err != nil {
		return err
	}
	accountMap := make(map[string]Account)
	for _, dir := range dirs {
		bytes, err := ReadFile(accountDirPath + "/" + dir.Name())
		if err != nil {
			return err
		}
		lines := strings.Split(string(bytes), "\n")
		var temp Account
		for _, line := range lines {
			line = strings.TrimSpace(line) //去除文本前后空白
			if line != "" {
				if line[0] == ';' {
					// 跳过注释行
					continue
				} else {
					//非注释行
					words := strings.Fields(line)
					if len(words) >= 3 {
						key := words[2]
						temp = accountMap[key]
						account := Account{Acc: key, Type: nil, StartDate: "", EndDate: ""}
						if words[1] == "open" {
							// 最晚的开户日期设置为账户开户日期
							account.StartDate = getMaxDate(words[0], temp.StartDate)
							// 货币单位
							if len(words) >= 4 {
								account.Currency = words[3]
							}
						} else if words[1] == "close" {
							//账户最晚的关闭日期设置为账户关闭日期
							account.EndDate = getMaxDate(words[0], temp.EndDate)
						}
						if account.EndDate != "" && account.StartDate == getMaxDate(account.StartDate, account.EndDate) {
							// 如果结束时间非空，且 开户日期>关闭日期，则清空账户结束日期，设置此账户为有效账户
							account.EndDate = ""
							account.Status = true
						}
						// 现在如果结束日期非空，肯定满足有开始时间<结束时间
						if account.EndDate != "" {
							account.Status = false
						}
						if account.Currency == "" {
							account.Currency = temp.Currency
						}
						accountMap[key] = account
					}
				}
			}
		}
	}

	accounts := make([]Account, 0)
	for _, account := range accountMap {
		accounts = append(accounts, account)
	}
	// 账户按字母排序
	sort.Sort(AccountSort(accounts))
	ledgerAccountsMap[config.Id] = accounts
	LogSystemInfo(fmt.Sprintf("Success load [%s] accounts cache", config.Mail))
	return nil
}

func LoadLedgerAccountTypesMap(config Config) error {
	path := GetLedgerAccountTypeFilePath(config.DataPath)
	fileContent, err := ReadFile(path)
	if err != nil {
		return err
	}
	accountTypes := make(map[string]string)
	err = json.Unmarshal(fileContent, &accountTypes)
	if err != nil {
		LogSystemError("Failed unmarshal config file (" + path + ")")
		return err
	}
	if ledgerAccountTypesMap == nil {
		ledgerAccountTypesMap = make(map[string]map[string]string)
	}
	ledgerAccountTypesMap[config.Id] = accountTypes
	LogSystemInfo(fmt.Sprintf("Success load [%s] account type cache", config.Mail))
	return nil
}

func WriteLedgerConfigMap(newLedgerConfigMap map[string]Config) error {
	path := GetServerLedgerConfigFilePath()
	mapBytes, err := json.Marshal(ledgerConfigMap)
	if err != nil {
		LogSystemError("Failed marshal ConfigMap")
		return err
	}
	err = WriteFile(path, string(mapBytes))
	ledgerConfigMap = newLedgerConfigMap
	LogSystemInfo("Success write ledger_config file (" + path + ")")
	return err
}

func GenerateServerSecret(secret string) string {
	if secret == "" {
		serverSecret = RandChar(16)
	} else {
		serverSecret = secret
	}
	return serverSecret
}

func EqualServerSecret(secret string) bool {
	return serverSecret == secret
}

func LoadServerCurrencyMap() {
	if serverCurrencies == nil {
		serverCurrencies = make([]LedgerCurrency, 0)
	}
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "人民币", Currency: "CNY", Symbol: "¥"})
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "美元", Currency: "USD", Symbol: "$"})
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "欧元", Currency: "EUR", Symbol: "€"})
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "日元", Currency: "JPY", Symbol: "¥"})
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "加拿大元", Currency: "CAD", Symbol: "$"})
	serverCurrencies = append(serverCurrencies, LedgerCurrency{Name: "俄罗斯卢布", Currency: "RUB", Symbol: "₽"})
}

func LoadLedgerCurrencyMap(config *Config) error {
	LoadServerCurrencyMap()
	path := GetLedgerCurrenciesFilePath(config.DataPath)
	if !FileIfExist(path) {
		err := CreateFile(path)
		if err != nil {
			return err
		}

		bytes, err := json.Marshal(serverCurrencies)
		if err != nil {
			return err
		}
		err = WriteFile(path, string(bytes))
		if err != nil {
			return err
		}
	}

	fileContent, err := ReadFile(path)
	if err != nil {
		return err
	}
	var currencies []LedgerCurrency
	err = json.Unmarshal(fileContent, &currencies)
	if err != nil {
		LogSystemError("Failed unmarshal config file (" + path + ")")
		return err
	}
	if ledgerCurrencyMap == nil {
		ledgerCurrencyMap = make(map[string][]LedgerCurrency)
	}
	ledgerCurrencyMap[config.Id] = currencies
	LogSystemInfo(fmt.Sprintf("Success load [%s] account type cache", config.Mail))
	// 刷新汇率
	RefreshLedgerCurrency(config)
	return nil
}

func GetLedgerCurrency(ledgerId string) []LedgerCurrency {
	return ledgerCurrencyMap[ledgerId]
}

type CommodityPrice struct {
	Date      string `json:"date"`
	Commodity string `json:"commodity"`
	Currency  string `json:"operatingCurrency"`
	Value     string `json:"value"`
}

func newCommodityPriceListFromString(lines []string) []CommodityPrice {
	commodityPriceList := make([]CommodityPrice, 0, len(lines))
	// foreach lines
	for _, line := range lines {
		if strings.Trim(line, " ") == "" {
			continue
		}
		// split line by " "
		words := strings.Fields(line)
		commodityPriceList = append(commodityPriceList, CommodityPrice{
			Date:      words[0],
			Commodity: words[2],
			Value:     words[3],
			Currency:  words[4],
		})
	}
	return commodityPriceList
}

func RefreshLedgerCurrency(ledgerConfig *Config) []LedgerCurrency {
	// 查询货币获取当前汇率
	statsPricesResultList := BeanReportAllPrices(ledgerConfig)
	// statsPricesResultList 转为 map
	existCurrencyMap := make(map[string]CommodityPrice)
	for _, statsPricesResult := range statsPricesResultList {
		existCurrencyMap[statsPricesResult.Commodity] = statsPricesResult
	}

	result := make([]LedgerCurrency, 0)
	currencies := GetLedgerCurrency(ledgerConfig.Id)
	for _, c := range currencies {
		current := c.Currency == ledgerConfig.OperatingCurrency
		var price string
		var date string
		if current {
			price = "1"
			date = time.Now().Format("2006-01-02")
		} else {
			value, exists := existCurrencyMap[c.Currency]
			if exists {
				price = value.Value
				date = value.Date
			}
		}
		result = append(result, LedgerCurrency{
			Name:      c.Name,
			Currency:  c.Currency,
			Symbol:    c.Symbol,
			Current:   current,
			Price:     price,
			PriceDate: date,
		})
	}
	// 刷新账本货币缓存
	ledgerCurrencyMap[ledgerConfig.Id] = result
	return result
}

func GetLedgerCurrencyMap(ledgerId string) map[string]LedgerCurrency {
	currencyMap := make(map[string]LedgerCurrency)
	currencies := GetLedgerCurrency(ledgerId)
	if currencies == nil {
		return currencyMap
	}
	for _, currency := range currencies {
		currencyMap[currency.Currency] = currency
	}
	return currencyMap
}

func GetCommoditySymbol(ledgerId string, commodity string) string {
	currencyMap := GetLedgerCurrencyMap(ledgerId)
	if currencyMap == nil {
		return commodity
	}
	if _, ok := currencyMap[commodity]; !ok {
		return commodity
	}
	return currencyMap[commodity].Symbol
}

func GetServerCommoditySymbol(commodity string) string {
	for _, currency := range serverCurrencies {
		if currency.Currency == commodity {
			return currency.Symbol
		}
	}
	return commodity
}

func GetAccountPrefix(account string) string {
	nodes := strings.Split(account, ":")
	return nodes[0]
}

func GetAccountName(account string) string {
	nodes := strings.Split(account, ":")
	return nodes[len(nodes)-1]
}

func GetAccountIconName(account string) string {
	nodes := strings.Split(account, ":")
	return strings.Join(nodes, "_")
}
