package accesslogs

import (
	"encoding/json"
	"github.com/TeaOSLab/EdgeAPI/internal/db/models"
	"github.com/TeaOSLab/EdgeAPI/internal/errors"
	"github.com/TeaOSLab/EdgeAPI/internal/remotelogs"
	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
	"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
	"github.com/iwind/TeaGo/Tea"
	"github.com/iwind/TeaGo/lists"
	"github.com/iwind/TeaGo/types"
	"sync"
	"time"
)

var SharedStorageManager = NewStorageManager()

type StorageManager struct {
	storageMap map[int64]StorageInterface // policyId => Storage

	locker sync.Mutex
}

func NewStorageManager() *StorageManager {
	return &StorageManager{
		storageMap: map[int64]StorageInterface{},
	}
}

func (this *StorageManager) Start() {
	var ticker = time.NewTicker(1 * time.Minute)
	if Tea.IsTesting() {
		ticker = time.NewTicker(5 * time.Second)
	}

	// 启动时执行一次
	var err = this.Loop()
	if err != nil {
		remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
	}

	// 循环执行
	for range ticker.C {
		err := this.Loop()
		if err != nil {
			remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "update error: "+err.Error())
		}
	}
}

// 写入日志
func (this *StorageManager) Write(policyId int64, accessLogs []*pb.HTTPAccessLog) error {
	this.locker.Lock()
	storage, ok := this.storageMap[policyId]
	this.locker.Unlock()

	if !ok {
		return nil
	}

	if !storage.IsOk() {
		return nil
	}

	return storage.Write(accessLogs)
}

// Loop 更新
func (this *StorageManager) Loop() error {
	policies, err := models.SharedHTTPAccessLogPolicyDAO.FindAllEnabledAndOnPolicies(nil)
	if err != nil {
		return err
	}
	var policyIds = []int64{}
	for _, policy := range policies {
		if policy.IsOn == 1 {
			policyIds = append(policyIds, int64(policy.Id))
		}
	}

	this.locker.Lock()
	defer this.locker.Unlock()

	// 关闭不用的
	for policyId, storage := range this.storageMap {
		if !lists.ContainsInt64(policyIds, policyId) {
			err := storage.Close()
			if err != nil {
				remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close '"+types.String(policyId)+"' failed: "+err.Error())
			}
			delete(this.storageMap, policyId)
			remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "remove '"+types.String(policyId)+"'")
		}
	}

	for _, policy := range policies {
		var policyId = int64(policy.Id)
		storage, ok := this.storageMap[policyId]
		if ok {
			// 检查配置是否有变更
			if types.Int(policy.Version) != storage.Version() {
				err = storage.Close()
				if err != nil {
					remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "close policy '"+types.String(policyId)+"' failed: "+err.Error())

					// 继续往下执行
				}

				if len(policy.Options) > 0 {
					err = json.Unmarshal([]byte(policy.Options), storage.Config())
					if err != nil {
						remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "unmarshal policy '"+types.String(policyId)+"' config failed: "+err.Error())
						storage.SetOk(false)
						continue
					}
				}

				storage.SetVersion(types.Int(policy.Version))
				err := storage.Start()
				if err != nil {
					remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
					continue
				}
				storage.SetOk(true)
				remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "restart policy '"+types.String(policyId)+"'")
			}
		} else {
			storage, err := this.createStorage(policy.Type, []byte(policy.Options))
			if err != nil {
				remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "create policy '"+types.String(policyId)+"' failed: "+err.Error())
				continue
			}
			storage.SetVersion(types.Int(policy.Version))
			this.storageMap[policyId] = storage
			err = storage.Start()
			if err != nil {
				remotelogs.Error("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"' failed: "+err.Error())
				continue
			}
			storage.SetOk(true)
			remotelogs.Println("ACCESS_LOG_STORAGE_MANAGER", "start policy '"+types.String(policyId)+"'")
		}
	}

	return nil
}

func (this *StorageManager) createStorage(storageType string, optionsJSON []byte) (StorageInterface, error) {
	switch storageType {
	case serverconfigs.AccessLogStorageTypeFile:
		var config = &serverconfigs.AccessLogFileStorageConfig{}
		if len(optionsJSON) > 0 {
			err := json.Unmarshal(optionsJSON, config)
			if err != nil {
				return nil, err
			}
		}
		return NewFileStorage(config), nil
	case serverconfigs.AccessLogStorageTypeES:
		var config = &serverconfigs.AccessLogESStorageConfig{}
		if len(optionsJSON) > 0 {
			err := json.Unmarshal(optionsJSON, config)
			if err != nil {
				return nil, err
			}
		}
		return NewESStorage(config), nil
	case serverconfigs.AccessLogStorageTypeTCP:
		var config = &serverconfigs.AccessLogTCPStorageConfig{}
		if len(optionsJSON) > 0 {
			err := json.Unmarshal(optionsJSON, config)
			if err != nil {
				return nil, err
			}
		}
		return NewTCPStorage(config), nil
	case serverconfigs.AccessLogStorageTypeSyslog:
		var config = &serverconfigs.AccessLogSyslogStorageConfig{}
		if len(optionsJSON) > 0 {
			err := json.Unmarshal(optionsJSON, config)
			if err != nil {
				return nil, err
			}
		}
		return NewSyslogStorage(config), nil
	case serverconfigs.AccessLogStorageTypeCommand:
		var config = &serverconfigs.AccessLogCommandStorageConfig{}
		if len(optionsJSON) > 0 {
			err := json.Unmarshal(optionsJSON, config)
			if err != nil {
				return nil, err
			}
		}
		return NewCommandStorage(config), nil
	}

	return nil, errors.New("invalid policy type '" + storageType + "'")
}
