package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	// "github.com/davecgh/go-spew/spew"
)

func init() {
}

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func main() {
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	pipeProcessor(processInput)
}

func processHit(nginxDate, ipAddress, payload, ua, xsollaUID string, nginxMsec string) (map[string]interface{}, error) {
	var err error
	event := make(map[string]interface{}, 5)

	payload, err = strconv.Unquote(fmt.Sprintf(`"%s"`, payload))
	if err != nil {
		return event, err
	}
	if payload == "" {
		return event, err
	}

	_ = json.Unmarshal([]byte(payload), &event)

	var val interface{}
	for k, v := range event {
		if v == nil {
			delete(event, k)
		} else {
			switch v.(type) {
			// большие инты преобразуются во флоат, поэтому нужен хак для правильного перевода
			case float64:
				val = strconv.FormatFloat(v.(float64), 'f', -1, 64)
			// обработка массивов данных
			case []interface{}:
				var tempSlice []interface{}
				for _, sliceVal := range v.([]interface{}) {
					switch sliceVal.(type) {
					case float64:
						sliceVal = strconv.FormatFloat(sliceVal.(float64), 'f', -1, 64)
					default:
						sliceVal = fmt.Sprintf("%v", sliceVal)
					}
					tempSlice = append(tempSlice, sliceVal)
				}
				val = tempSlice
			default:
				val = fmt.Sprintf("%v", v)
			}
			event[k] = val
		}
	}

	d := nginxDate[:10]
	dt := d + " " + nginxDate[11:19]
	event["internal_date"] = d
	event["internal_time"] = dt

	if len(nginxMsec) > 0 {
		event["internal_msec"] = strings.Replace(nginxMsec, ".", "", 1)
	}

	pos := strings.Index(ipAddress, ",")
	if pos > 0 {
		ipAddress = ipAddress[0:pos]
	}
	event["internal_ip"] = ipAddress
	event["XsollaUID"] = xsollaUID

	return event, err
}

func processInput(line string) (string, error) {
	in := strings.Split(line, "\t")
	// если в логе нет xsollauid - вставляем пустую строку
	if len(in) == 4 {
		in = append(in, "0")
	}

	if len(in) == 5 {
		in = append(in, "")
	}

	hit, err := processHit(in[0], in[1], in[2], in[3], in[4], in[5])
	if err != nil {
		return "", err
	}

	s, err := json.Marshal(hit)
	if err != nil {
		return "", err
	}

	return string(s), nil
}

func pipeProcessor(f func(string) (string, error)) {
	scanner := bufio.NewScanner(os.Stdin)
	writer := bufio.NewWriter(os.Stdout)

	var s string
	var err error
	for scanner.Scan() {
		s, err = f(scanner.Text())
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error:", err)
			continue
		}
		writer.WriteString(s)
		writer.WriteString("\n")
		writer.Flush()
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
	}
}
