package auditlog

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/opensearch-project/opensearch-go"
	log2 "github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic/pkg/config"
	"github.com/pingcap-inc/clinic/pkg/es"
)

var (
	auditlogmapping = `{
  "mappings": {
    "properties": {
      "email": {
        "type": "keyword"
      },
      "username": {
        "type": "text",
        "fields": {
          "keyword": {"type": "keyword", "ignore_above": 256}
        }
      },
      "resource": {
        "type": "keyword"
      },
      "subject": {
        "type": "keyword"
      },
      "operator": {
        "type": "keyword"
      },
      "content": {
         "type": "text"
      },
      "timestamp": {
        "type": "date"
      }
    }
  }
}`

	index = "auditlog"
)

type AuditLogClient struct {
	cli *opensearch.Client
}

func NewAuditLogClient(cfg config.LogConfig) (*AuditLogClient, error) {
	cli, err := es.AwsEsClient(cfg.Endpoint, cfg.User, cfg.Password)
	if err != nil {
		return nil, err
	}
	if err := es.CreateIndexIfAbsentAws(cli, index, auditlogmapping); err != nil {
		return nil, err
	}
	// es.CreateIndexPatternIfAbsent(cfg, index)

	return &AuditLogClient{
		cli: cli,
	}, nil
}

type AuditLog struct {
	Email     string `json:"email"`
	UserName  string `json:"username"`
	Resource  string `json:"resource"`
	Operator  string `json:"operator"`
	Timestamp int64  `json:"timestamp"`
	Subject   string `json:"subject"`
}

func (cli *AuditLogClient) Do(auditLog *AuditLog) {
	go func() {
		if err := cli.do(auditLog); err != nil {
			log2.Error("insert auditlog failed", zap.Error(err))
		}
	}()
}

func (cli *AuditLogClient) do(log *AuditLog) error {
	data, err := json.Marshal(log)
	if err != nil {
		return err
	}

	resp, err := cli.cli.Index(index, bytes.NewReader(data))
	if err != nil {
		return err
	}

	defer resp.Body.Close()
	if resp.IsError() {
		var e map[string]any
		if err := json.NewDecoder(resp.Body).Decode(&e); err != nil {
			return err
		} else {
			return fmt.Errorf("insert auditlog failed. status=%s, type=%s, reason=%s.", resp.Status(), e["error"].(map[string]any)["type"], e["error"].(map[string]any)["reason"])
		}
	}

	return nil
}
