package utils

import (
	"easyProxy/utils"
	"encoding/csv"
	"errors"
	"github.com/astaxie/beego"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"
)

var (
	CsvDb *Csv
	once  sync.Once
)

type Flow struct {
	ExportFlow int64 //出口流量
	InletFlow  int64 //入口流量
	FlowLimit  int64 //流量限制，出口+入口 /M
}

type Client struct {
	Cnf       *Config
	Id        int    //id
	VerifyKey string //验证密钥
	Addr      string //客户端ip地址
	Remark    string //备注
	Status    bool   //是否开启
	IsConnect bool   //是否连接
	RateLimit int    //速度限制 /kb
	Flow      *Flow  //流量
	Rate      *Rate  //速度控制
}

type Tunnel struct {
	Id           int     //Id
	TcpPort      int     //服务端与客户端通信端口
	Mode         string  //启动方式
	Target       string  //目标
	Status       bool    //是否开启
	Client       *Client //所属客户端id
	Flow         *Flow
	Config       *Config
	UseClientCnf bool   //是否继承客户端配置
	Remark       string //备注
}

type Config struct {
	U              string //socks5验证用户名
	P              string //socks5验证密码
	Compress       string //压缩方式
	Crypt          bool   //是否加密
	Mux            bool   //是否加密
	CompressEncode int    //加密方式
	CompressDecode int    //解密方式
}

type Host struct {
	Host         string //启动方式
	Target       string //目标
	HeaderChange string //host修改
	HostChange   string //host修改
	Flow         *Flow
	Client       *Client
	Remark       string //备注
}

func NewCsv() *Csv {
	c := new(Csv)
	return c
}

type Csv struct {
	Tasks            []*Tunnel
	Path             string
	Hosts            []*Host   //域名列表
	Clients          []*Client //客户端
	ClientIncreaseId int       //客户端id
	TaskIncreaseId   int       //任务自增ID
	sync.Mutex
}

func (s *Csv) Init() {
	s.LoadClientFromCsv()
	s.LoadTaskFromCsv()
	s.LoadHostFromCsv()
}

func (s *Csv) StoreTasksToCsv() {
	// 创建文件
	csvFile, err := os.Create(beego.AppPath + "/conf/tasks.csv")
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer csvFile.Close()
	writer := csv.NewWriter(csvFile)
	for _, task := range s.Tasks {
		record := []string{
			strconv.Itoa(task.TcpPort),
			task.Mode,
			task.Target,
			task.Config.U,
			task.Config.P,
			task.Config.Compress,
			utils.GetStrByBool(task.Status),
			GetStrByBool(task.Config.Crypt),
			GetStrByBool(task.Config.Mux),
			strconv.Itoa(task.Config.CompressEncode),
			strconv.Itoa(task.Config.CompressDecode),
			strconv.Itoa(task.Id),
			strconv.Itoa(task.Client.Id),
			strconv.FormatBool(task.UseClientCnf),
			task.Remark,
		}
		err := writer.Write(record)
		if err != nil {
			log.Fatalf(err.Error())
		}
	}
	writer.Flush()
}

func (s *Csv) openFile(path string) ([][]string, error) {
	// 打开文件
	file, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 获取csv的reader
	reader := csv.NewReader(file)

	// 设置FieldsPerRecord为-1
	reader.FieldsPerRecord = -1

	// 读取文件中所有行保存到slice中
	return reader.ReadAll()
}

func (s *Csv) LoadTaskFromCsv() {
	path := beego.AppPath + "/conf/tasks.csv"
	records, err := s.openFile(path)
	if err != nil {
		log.Fatal("配置文件打开错误:", path)
	}
	var tasks []*Tunnel
	// 将每一行数据保存到内存slice中
	for _, item := range records {
		post := &Tunnel{
			TcpPort: GetIntNoErrByStr(item[0]),
			Mode:    item[1],
			Target:  item[2],
			Config: &Config{
				U:              item[3],
				P:              item[4],
				Compress:       item[5],
				Crypt:          GetBoolByStr(item[7]),
				Mux:            GetBoolByStr(item[8]),
				CompressEncode: GetIntNoErrByStr(item[9]),
				CompressDecode: GetIntNoErrByStr(item[10]),
			},
			Status:       utils.GetBoolByStr(item[6]),
			Id:           GetIntNoErrByStr(item[11]),
			UseClientCnf: GetBoolByStr(item[13]),
			Remark:       item[14],
		}
		post.Flow = new(Flow)
		if post.Client, err = s.GetClient(GetIntNoErrByStr(item[12])); err != nil {
			continue
		}
		tasks = append(tasks, post)
		if post.Id > s.TaskIncreaseId {
			s.TaskIncreaseId = post.Id
		}
	}
	s.Tasks = tasks
}

func (s *Csv) GetTaskId() int {
	s.Lock()
	defer s.Unlock()
	s.TaskIncreaseId++
	return s.TaskIncreaseId
}

func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (int, error) {
	s.Lock()
	defer s.Unlock()
	for _, v := range s.Clients {
		if utils.Getverifyval(v.VerifyKey) == vKey && v.Status {
			if arr := strings.Split(addr, ":"); len(arr) > 0 {
				v.Addr = arr[0]
			}
			return v.Id, nil
		}
	}
	return 0, errors.New("not found")
}

func (s *Csv) NewTask(t *Tunnel) {
	t.Flow = new(Flow)
	s.Tasks = append(s.Tasks, t)
	s.StoreTasksToCsv()
}

func (s *Csv) UpdateTask(t *Tunnel) error {
	for k, v := range s.Tasks {
		if v.Id == t.Id {
			s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
			s.Tasks = append(s.Tasks, t)
			s.StoreTasksToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) DelTask(id int) error {
	for k, v := range s.Tasks {
		if v.Id == id {
			s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
			s.StoreTasksToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) GetTask(id int) (v *Tunnel, err error) {
	for _, v = range s.Tasks {
		if v.Id == id {
			return
		}
	}
	err = errors.New("未找到")
	return
}

func (s *Csv) StoreHostToCsv() {
	// 创建文件
	csvFile, err := os.Create(beego.AppPath + "/conf/hosts.csv")
	if err != nil {
		panic(err)
	}
	defer csvFile.Close()
	// 获取csv的Writer
	writer := csv.NewWriter(csvFile)
	// 将map中的Post转换成slice，因为csv的Write需要slice参数
	// 并写入csv文件
	for _, host := range s.Hosts {
		record := []string{
			host.Host,
			host.Target,
			strconv.Itoa(host.Client.Id),
			host.HeaderChange,
			host.HostChange,
			host.Remark,
		}
		err1 := writer.Write(record)
		if err1 != nil {
			panic(err1)
		}
	}
	// 确保所有内存数据刷到csv文件
	writer.Flush()
}

func (s *Csv) LoadClientFromCsv() {
	path := beego.AppPath + "/conf/clients.csv"
	records, err := s.openFile(path)
	if err != nil {
		log.Fatal("配置文件打开错误:", path)
	}
	var clients []*Client
	// 将每一行数据保存到内存slice中
	for _, item := range records {
		post := &Client{
			Id:        GetIntNoErrByStr(item[0]),
			VerifyKey: item[1],
			Remark:    item[2],
			Status:    GetBoolByStr(item[3]),
			RateLimit: GetIntNoErrByStr(item[9]),
			Cnf: &Config{
				U:        item[4],
				P:        item[5],
				Crypt:    GetBoolByStr(item[6]),
				Mux:      GetBoolByStr(item[7]),
				Compress: item[8],
			},
		}
		if post.Id > s.ClientIncreaseId {
			s.ClientIncreaseId = post.Id
		}
		if post.RateLimit > 0 {
			post.Rate = NewRate(int64(post.RateLimit * 1024))
			post.Rate.Start()
		}
		post.Flow = new(Flow)
		post.Flow.FlowLimit = int64(utils.GetIntNoerrByStr(item[10]))
		clients = append(clients, post)
	}
	s.Clients = clients
}

func (s *Csv) LoadHostFromCsv() {
	path := beego.AppPath + "/conf/hosts.csv"
	records, err := s.openFile(path)
	if err != nil {
		log.Fatal("配置文件打开错误:", path)
	}
	var hosts []*Host
	// 将每一行数据保存到内存slice中
	for _, item := range records {
		post := &Host{
			Host:         item[0],
			Target:       item[1],
			HeaderChange: item[3],
			HostChange:   item[4],
			Remark:       item[5],
		}
		if post.Client, err = s.GetClient(GetIntNoErrByStr(item[2])); err != nil {
			continue
		}
		post.Flow = new(Flow)
		hosts = append(hosts, post)
	}
	s.Hosts = hosts
}

func (s *Csv) DelHost(host string) error {
	for k, v := range s.Hosts {
		if v.Host == host {
			s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
			s.StoreHostToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) NewHost(t *Host) {
	t.Flow = new(Flow)
	s.Hosts = append(s.Hosts, t)
	s.StoreHostToCsv()

}

func (s *Csv) UpdateHost(t *Host) error {
	for k, v := range s.Hosts {
		if v.Host == t.Host {
			s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
			s.Hosts = append(s.Hosts, t)
			s.StoreHostToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) GetHost(start, length int, id int) ([]*Host, int) {
	list := make([]*Host, 0)
	var cnt int
	for _, v := range s.Hosts {
		if id == 0 || v.Client.Id == id {
			cnt++
			if start--; start < 0 {
				if length--; length > 0 {
					list = append(list, v)
				}
			}
		}
	}
	return list, cnt
}

func (s *Csv) DelClient(id int) error {
	for k, v := range s.Clients {
		if v.Id == id {
			s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
			s.StoreClientsToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) NewClient(c *Client) {
	s.Lock()
	defer s.Unlock()
	c.Flow = new(Flow)
	s.Clients = append(s.Clients, c)
	s.StoreClientsToCsv()
}

func (s *Csv) GetClientId() int {
	s.Lock()
	defer s.Unlock()
	s.ClientIncreaseId++
	return s.ClientIncreaseId
}

func (s *Csv) UpdateClient(t *Client) error {
	s.Lock()
	defer s.Unlock()
	for k, v := range s.Clients {
		if v.Id == t.Id {
			s.Clients = append(s.Clients[:k], s.Clients[k+1:]...)
			s.Clients = append(s.Clients, t)
			s.StoreClientsToCsv()
			return nil
		}
	}
	return errors.New("不存在")
}

func (s *Csv) GetClientList(start, length int) ([]*Client, int) {
	list := make([]*Client, 0)
	var cnt int
	for _, v := range s.Clients {
		cnt++
		if start--; start < 0 {
			if length--; length > 0 {
				list = append(list, v)
			}
		}
	}
	return list, cnt
}

func (s *Csv) GetClient(id int) (v *Client, err error) {
	for _, v = range s.Clients {
		if v.Id == id {
			return
		}
	}
	err = errors.New("未找到")
	return
}
func (s *Csv) StoreClientsToCsv() {
	// 创建文件
	csvFile, err := os.Create(beego.AppPath + "/conf/clients.csv")
	if err != nil {
		log.Fatalf(err.Error())
	}
	defer csvFile.Close()
	writer := csv.NewWriter(csvFile)
	for _, client := range s.Clients {
		record := []string{
			strconv.Itoa(client.Id),
			client.VerifyKey,
			client.Remark,
			strconv.FormatBool(client.Status),
			client.Cnf.U,
			client.Cnf.P,
			utils.GetStrByBool(client.Cnf.Crypt),
			utils.GetStrByBool(client.Cnf.Mux),
			client.Cnf.Compress,
			strconv.Itoa(client.RateLimit),
			strconv.Itoa(int(client.Flow.FlowLimit)),
		}
		err := writer.Write(record)
		if err != nil {
			log.Fatalf(err.Error())
		}
	}
	writer.Flush()
}

//init csv from file
func GetCsvDb() *Csv {
	once.Do(func() {
		CsvDb = NewCsv()
		CsvDb.Init()
	})
	return CsvDb
}

//深拷贝Tunnel
func DeepCopyConfig(c *Config) *Config {
	return &Config{
		U:              c.U,
		P:              c.P,
		Compress:       c.Compress,
		Crypt:          c.Crypt,
		Mux:            c.Mux,
		CompressEncode: c.CompressEncode,
		CompressDecode: c.CompressDecode,
	}
}
