package icshttp

import (
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/big"
	"net/http"
	"strings"
	"sync"
	"time"

	"gitlab.com/cinnamon/voiceagent/icsconf"
	"gitlab.com/cinnamon/voiceagent/icslog"
	"gitlab.com/cinnamon/voiceagent/icspacketer"
	"gitlab.com/cinnamon/voiceagent/icspacketparser"
)

const HANDLE_NUM = 1024

const (
	TTS_FUNC1 = iota
	TTS_FUNC2
	TTS_FUNC3
	TTS_FUNC4
	BOT_FUNC
)

type Handler struct {
	r *Router
}

type handleInfo struct {
	id      int
	method  string
	pattern string
	h       http.HandlerFunc
}

type Resquest struct {
	Token          string `json:"token"`
	OprMngCode     string `json:"oprMngCode"`
	Method         string `json:"method"`
	TalkText       string `json:"talkText"`
	CallId         string `json:"callId"`
	TelNo          string `json:"telNo"`
	MentType       string `json:"mentType"`
	RecordFilePath string `json:"recordFilePath"`
}

type Response struct {
	ResultCode   int     `json:"resultCode"`
	Token        string  `json:"token"`
	Action       string  `json:"action"`
	AnounceMents string  `json:"announceMents"`
	Data         ResData `json:"DATA"`
}

type ResData struct {
	BargeIn      string `json:"bargeIn"`
	RecodingFile string `json:"recodingFile"`
	SttMaxTime   int    `json:"sttMaxTime"`
	MaxDigit     int    `json:"maxDigit"`
	MinDigit     int    `json:"minDigit"`
	DigitTerm    int    `json:"digitTerm"`
	TelNo        string `json:"telNo"`
}

type ResStatus struct {
	Count         int
	Status        string
	NotUnderstand int
}

type ScenarioSession struct {
	session map[string]ResStatus
	m       *sync.Mutex
}

// var scenarioSession map[string]ResStatus

var handles []*handleInfo
var session []*ScenarioSession

func init() {
	handles = make([]*handleInfo, HANDLE_NUM)
	// session := make(map[string]ResStatus) // session init
	scenarioSession := new(ScenarioSession)
	scenarioSession.session = make(map[string]ResStatus, 200)
	scenarioSession.m = &sync.Mutex{}
	// session = make(map[string]ResStatus) // session init

	//TTS
	handles[TTS_FUNC1] = &handleInfo{method: "GET", pattern: "/tts/1", h: TTSFunc1}
	handles[TTS_FUNC2] = &handleInfo{method: "GET", pattern: "/tts/2", h: TTSFunc2}
	handles[TTS_FUNC3] = &handleInfo{method: "POST", pattern: "/tts/3", h: TTSFunc3}
	handles[TTS_FUNC4] = &handleInfo{method: "POST", pattern: "/tts/4", h: TTSFunc4}

	// bot sinario
	handles[BOT_FUNC] = &handleInfo{method: "POST", pattern: "/platform/api/call/process", h: scenarioSession.BOTPFunc}
}

func BuildHandler(r *Router) {
	for iter, handle := range handles {
		if handle != nil {
			//fmt.Println(handle)
			handle.id = iter
			r.HandleFunc(handle.method, handle.pattern, handle.h)
		} else {
			break
		}
	}
}

/////////////////////////////////////////////
//TTS

func TTSFunc1(w http.ResponseWriter, r *http.Request) {
	h := icspacketparser.NewHTTP()
	err := icspacketer.PutPacket(r, &h)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Fprintln(w, "TTS func 1", r)
}

func TTSFunc2(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "TTS func 2", r)
}

//post
func TTSFunc3(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "TTS func 3", r)
	body := make([]byte, r.ContentLength)
	r.Body.Read(body)
	fmt.Println("TTS func 3", r.Method, r.URL)
	for iter, v := range r.Header {
		fmt.Println(iter, v)
	}
	fmt.Println(string(body))
}

//post
func TTSFunc4(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "TTS func 4", r)
}

/////////////////////////////////////////////
//BOT

func (s *ScenarioSession) BOTPFunc(w http.ResponseWriter, r *http.Request) {
	// get scenario config
	l := icslog.GetIcsLog()
	conf := icsconf.GetIcsConfig()
	scnarioConf := conf.ScenarioConfig

	// Request json parsing
	request := Resquest{}
	resBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println(err)
	}
	defer r.Body.Close()

	err = json.Unmarshal(resBody, &request)
	if err != nil {
		l.Printf(icslog.LOG_LEVEL_INFO, -1, "response unmarshal error!!!!! ")
	}

	agent := strings.SplitN(request.CallId, "@", 2)
	fmt.Printf(">>>>>> DATA INFO - method [%s], agent [%s], token [%s]\n", request.Method, agent, request.Token)
	l.Printf(icslog.LOG_LEVEL_INFO, -1, ">>>>>> DATA INFO - method [%s], agent [%s], token [%s] ", request.Method, agent, request.Token)

	response := new(Response)

	////////////////////////////////////
	///////////// EXCEPT ///////////////
	////////////////////////////////////
	if conf.ExceptTest.TestInfo.Value {
		// eType := conf.ExceptTest.TestInfo.Type

		response.ResultCode = conf.ExceptTest.TestInfo.ResultCode
		response.Token = conf.ExceptTest.TestInfo.Token
		response.Action = conf.ExceptTest.TestInfo.Action
		response.AnounceMents = conf.ExceptTest.TestInfo.Announcement
		response.Data.BargeIn = conf.ExceptTest.TestInfo.Bargein
		response.Data.RecodingFile = conf.ExceptTest.TestInfo.Recodingfile
		response.Data.SttMaxTime = conf.ExceptTest.TestInfo.Sttmaxtime
		// switch eType {
		// case "sizeup":
		// 	response.ResultCode = 200
		// 	response.Token = request.Token + "000000000000000000000000000000"
		// 	response.Action = ""
		// 	response.AnounceMents = ""
		// 	response.Data.BargeIn = ""
		// 	response.Data.RecodingFile = ""
		// 	response.Data.SttMaxTime = 10
		// case "sizedown":
		// case "space":
		// case "null":
		// }
	}

	////////////////////////////////////
	//////// SCENARIO MAPPING///////////
	////////////////////////////////////

	// 현재 들어온 method로 분기
	// next 시나리오로 넘겨주기
	fmt.Printf("response %+v", request)
	if request.Method == "HANGUP" {
		response.ResultCode = 200
		response.Token = request.Token
		response.Action = ""
		response.AnounceMents = ""
		response.Data.BargeIn = ""
		response.Data.RecodingFile = ""
		response.Data.SttMaxTime = 10
	} else if request.Method != "INIT" && request.TalkText == "" {
		response.ResultCode = 200
		response.Token = request.Token
		if s.session[request.Token].NotUnderstand == 3 {
			response.AnounceMents = "잘 이해하지 못했습니다. 정확한 상담을 위해 매장으로 연결해드릴게요."
			response.Action = "END"
		} else {
			response.AnounceMents = "잘 이해하지 못했습니다. 다시 말씀해주세요."
			response.Action = "STT"
			s.session[request.Token] = ResStatus{Count: 1, Status: scnarioConf.Order[0], NotUnderstand: s.session[request.Token].NotUnderstand + 1}
		}
		response.Data.BargeIn = ""
		response.Data.RecodingFile = ""
		response.Data.SttMaxTime = 10
	} else {
		if request.Method == "HANGUP" {
			s.ByeCheck(request.Method, request.Token)
		} else {
			switch request.Method {
			case "INIT":
				token := createToken(request.CallId)
				response.ResultCode = 200
				response.Token = token
				response.Action = scnarioConf.Order[1]
				response.AnounceMents = scnarioConf.Value[0]
				response.Data.BargeIn = scnarioConf.BargeIn[0]
				response.Data.RecodingFile = "Y"
				response.Data.SttMaxTime = scnarioConf.SttMaxTime[0]

				s.m.Lock()
				s.session[token] = ResStatus{Count: 1, Status: scnarioConf.Order[0], NotUnderstand: 0}
				s.m.Unlock()
			case "STT":
				response.ResultCode = 200
				response.Token = request.Token
				response.Action = scnarioConf.Order[s.session[request.Token].Count+1]
				response.AnounceMents = scnarioConf.Value[s.session[request.Token].Count]
				response.Data.BargeIn = scnarioConf.BargeIn[s.session[request.Token].Count]
				response.Data.RecodingFile = "Y"
				response.Data.SttMaxTime = scnarioConf.SttMaxTime[s.session[request.Token].Count]

				s.m.Lock()
				s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]}
				s.m.Unlock()
			case "DTMF":
				response.ResultCode = 200
				response.Token = request.Token
				response.Action = scnarioConf.Order[s.session[request.Token].Count+1]
				response.AnounceMents = scnarioConf.Value[s.session[request.Token].Count]
				response.Data.BargeIn = "N"
				response.Data.RecodingFile = "Y"
				response.Data.SttMaxTime = 3

				s.m.Lock()
				s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]}
				s.m.Unlock()
			case "REFER":
				response.ResultCode = 200
				response.Token = request.Token
				response.Action = "END"
				response.AnounceMents = "네. 매장으로 연결해드릴게요."
				response.Data.BargeIn = ""
				response.Data.RecodingFile = ""
				response.Data.SttMaxTime = 0
				response.Data.TelNo = request.TelNo

				s.m.Lock()
				s.session[request.Token] = ResStatus{Count: s.session[request.Token].Count + 1, Status: scnarioConf.Order[s.session[request.Token].Count+1]}
				s.m.Unlock()
			case "BYE": // 전화 종료 요청
				response.ResultCode = 200
				response.Token = request.Token
				response.Action = "END"
				response.AnounceMents = "접수가 완료되었습니다."
				response.Data.BargeIn = "Y"
				response.Data.RecodingFile = "Y"
				response.Data.SttMaxTime = 0
				// case "HANGUP":
				// 	response.ResultCode = 200
				// 	response.Token = request.Token
				// 	response.Action = ""
				// 	response.AnounceMents = ""
				// 	response.Data.BargeIn = ""
				// 	response.Data.RecodingFile = ""
				// 	response.Data.SttMaxTime = 0
				// 	if ByeCheck(request.Method, request.Token) {
				// 	}
			}
		}
	}

	// response marshal
	resMarshal, jerr := json.Marshal(response)
	if jerr != nil {
		fmt.Println("Json Marshal error ", jerr)
	}
	l.Printf(icslog.LOG_LEVEL_INFO, -1, "Send Message\n %s", string(resMarshal))

	if scnarioConf.TimeValue[s.session[request.Token].Count] {
		l.Printf(icslog.LOG_LEVEL_INFO, -1, "Time Sleep %d \r\n", scnarioConf.TimeSleep[s.session[request.Token].Count])
		time.Sleep(time.Second * time.Duration(time.Duration(scnarioConf.TimeSleep[s.session[request.Token].Count])))
	}

	// send response
	fmt.Fprintln(w, string(resMarshal))

}

func createToken(callId string) string {
	token := ""
	// length := []int{8, 4, 4, 4, 12}
	// codeAlphabet := "abcdefghijklmnopqrstuvwxyz"
	// codeAlphabet += "0123456789"

	// for i, v := range length {
	// 	fmt.Println(i, v)
	// 	for j := 0; j < v; j++ {
	// 		token += string(codeAlphabet[cryptoRandSecure(int64(len(codeAlphabet)))])
	// 	}
	// 	if v != 12 {
	// 		token += "-"
	// 	}
	// }
	times := fmt.Sprintf("%d", time.Now().UnixNano()/1000000)
	tokens := strings.Split(callId, "@")[0] + times
	token = tokens[0:8] + "-" + tokens[8:12] + "-" + tokens[12:16] + "-" + tokens[16:20] + "-" + tokens[20:32]
	return token
}

func cryptoRandSecure(max int64) int64 {
	nBig, err := rand.Int(rand.Reader, big.NewInt(max))
	if err != nil {
		fmt.Println(err)
	}
	return nBig.Int64()
}

func (s *ScenarioSession) ByeCheck(method string, token string) {
	l := icslog.GetIcsLog()
	// fmt.Printf(">>>>>> SESSION BYE INFO - token [%s]\n", token)
	l.Printf(icslog.LOG_LEVEL_INFO, -1, ">>>>>> SESSION BYE INFO - token [%s] ", token)
	s.m.Lock()
	delete(s.session, token)
	s.m.Unlock()
}
