package mihomoapi

import (
	"encoding/json"
	"fmt"
	"time"
)

type ConnectionSnapshot struct {
	Connections []Connection `json:"connections"`
}

type Connection struct {
	ID          string          `json:"id"`
	Metadata    ConnectionMeta  `json:"metadata"`
	Upload      int64           `json:"upload"`
	Download    int64           `json:"download"`
	Start       time.Time       `json:"start"`
	Chains      []string        `json:"chains"`
	Rule        string          `json:"rule"`
	RulePayload string          `json:"rulePayload"`
}

type ConnectionMeta struct {
	Network           string `json:"network"`
	Host              string `json:"host"`
	RemoteDestination string `json:"remoteDestination"`
	DestinationIP     string `json:"destinationIP"`
	DestinationPort   string `json:"destinationPort"`
	SniffHost         string `json:"sniffHost"`
}

func (c *Client) ConnectionSnapshot() (*ConnectionSnapshot, error) {
	var snap ConnectionSnapshot
	if err := c.getJSON("/connections", &snap); err != nil {
		return nil, err
	}
	return &snap, nil
}

// ParseConnectionSnapshot decodes Mihomo /connections JSON.
func ParseConnectionSnapshot(data []byte) (*ConnectionSnapshot, error) {
	var snap ConnectionSnapshot
	if err := json.Unmarshal(data, &snap); err != nil {
		return nil, err
	}
	return &snap, nil
}

func (conn *Connection) RequestHost() string {
	if conn.Metadata.Host != "" {
		return conn.Metadata.Host
	}
	if conn.Metadata.SniffHost != "" {
		return conn.Metadata.SniffHost
	}
	if conn.Metadata.RemoteDestination != "" {
		return conn.Metadata.RemoteDestination
	}
	if conn.Metadata.DestinationIP != "" {
		return fmt.Sprintf("%s:%s", conn.Metadata.DestinationIP, conn.Metadata.DestinationPort)
	}
	return ""
}

func (conn *Connection) RuleLine() string {
	if conn.Rule == "" {
		return ""
	}
	if conn.RulePayload == "" {
		return conn.Rule
	}
	return conn.Rule + "," + conn.RulePayload
}

func (conn *Connection) OutboundNode() string {
	return ResolveLeafOutbound(conn.Chains, nil)
}

func (conn *Connection) LeafOutbound(policyGroups map[string]bool) string {
	return ResolveLeafOutbound(conn.Chains, policyGroups)
}

func (conn *Connection) ChainString() string {
	if len(conn.Chains) == 0 {
		return ""
	}
	b, _ := json.Marshal(conn.Chains)
	return string(b)
}

func (conn *Connection) IsSuccess() bool {
	for _, ch := range conn.Chains {
		if ch == "REJECT" || ch == "REJECT-DROP" {
			return false
		}
	}
	return conn.Upload+conn.Download > 0
}
