package hosts
import (
   "bufio"
   "errors"
   "fmt"
   "io"
   "net"
   "os"
   "strings"
   "github.com/labring/sealos/pkg/utils/file"
   "github.com/labring/sealos/pkg/utils/logger"
   strings2 "github.com/labring/sealos/pkg/utils/strings"
   "github.com/emirpasic/gods/maps/linkedhashmap"
)
type HostFile struct {
   Path string
}
type hostname struct {
   Comment string
   Domain  string
   IP      string
}
func newHostname(comment string, domain string, ip string) *hostname {
   return &hostname{comment, domain, ip}
}
func (h *hostname) toString() string {
   return h.Comment + h.IP + " " + h.Domain + "\n"
}
func appendToFile(filePath string, hostname *hostname) {
   fp, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0644)
   if err != nil {
      logger.Warn("failed opening file %s : %s", filePath, err)
      return
   }
   defer fp.Close()
   _, err = fp.WriteString(hostname.toString())
   if err != nil {
      logger.Warn("failed append string: %s: %s", filePath, err)
      return
   }
}
func (h *HostFile) ParseHostFile(path string) (*linkedhashmap.Map, error) {
   if !file.IsExist(path) {
      logger.Warn("path %s is not exists", path)
      return nil, errors.New("path %s is not exists")
   }
   fp, fpErr := os.Open(path)
   if fpErr != nil {
      logger.Warn("open file '%s' failed", path)
      return nil, fmt.Errorf("open file '%s' failed ", path)
   }
   defer fp.Close()
   br := bufio.NewReader(fp)
   lm := linkedhashmap.New()
   curComment := ""
   for {
      str, rErr := br.ReadString('\n')
      if rErr == io.EOF {
         break
      }
      if len(str) == 0 || str == "\r\n" || strings2.IsEmptyLine(str) {
         continue
      }
      if str[0] == '#' {
         // 处理注释
         curComment += str
         continue
      }
      tmpHostnameArr := strings.Fields(str)
      curDomain := strings.Join(tmpHostnameArr[1:], " ")
      //if !iputils.CheckDomain(curDomain) {
      // return lm, errors.New(" file contain error domain" + curDomain)
      //}
      curIP := strings2.TrimSpaceWS(tmpHostnameArr[0])
      checkIP := net.ParseIP(curIP)
      if checkIP == nil {
         continue
      }
      tmpHostname := newHostname(curComment, curDomain, curIP)
      lm.Put(tmpHostname.Domain, tmpHostname)
      curComment = ""
   }
   return lm, nil
}
func (h *HostFile) AppendHost(domain string, ip string) {
   if domain == "" || ip == "" {
      return
   }
   hostname := newHostname("", domain, ip)
   appendToFile(h.Path, hostname)
}
func (h *HostFile) writeToFile(hostnameMap *linkedhashmap.Map, path string) {
   if !file.IsExist(path) {
      logger.Warn("path %s is not exists", path)
      return
   }
   fp, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
   if err != nil {
      logger.Warn("open file '%s' failed: %v", path, err)
      return
   }
   defer fp.Close()
   hostnameMap.Each(func(key interface{}, value interface{}) {
      if v, ok := value.(*hostname); ok {
         _, writeErr := fp.WriteString(v.toString())
         if writeErr != nil {
            logger.Warn(writeErr)
            return
         }
      }
   })
}
func (h *HostFile) DeleteDomain(domain string) {
   if domain == "" {
      return
   }
   currHostsMap, parseErr := h.ParseHostFile(h.Path)
   if parseErr != nil {
      logger.Warn("parse file failed" + parseErr.Error())
      return
   }
   _, found := currHostsMap.Get(domain)
   if currHostsMap == nil || !found {
      return
   }
   currHostsMap.Remove(domain)
   h.writeToFile(currHostsMap, h.Path)
}
func (h *HostFile) HasDomain(domain string) (string, bool) {
   if domain == "" {
      return "", false
   }
   currHostsMap, parseErr := h.ParseHostFile(h.Path)
   if parseErr != nil {
      logger.Warn("parse file failed" + parseErr.Error())
      return "", false
   }
   value, found := currHostsMap.Get(domain)
   if currHostsMap == nil || !found {
      return "", false
   }
   if v, ok := value.(*hostname); ok {
      return v.IP, true
   }
   return "", false
}
func (h *HostFile) ListCurrentHosts() {
   currHostsMap, parseErr := h.ParseHostFile(h.Path)
   if parseErr != nil {
      logger.Warn("parse file failed" + parseErr.Error())
      return
   }
   if currHostsMap == nil {
      return
   }
   currHostsMap.Each(func(key interface{}, value interface{}) {
      if v, ok := value.(*hostname); ok {
         fmt.Print(v.toString())
      }
   })
}