package main

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
)

var cookLeadID = "biukop_cl"

func crmpixelImage() (pixel []byte, err error) {
	b64pixel := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QcIBzEJ3JY/6wAAAAh0RVh0Q29tbWVudAD2zJa/AAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg=="
	pixel, err = base64.StdEncoding.DecodeString(b64pixel)
	return
}

func crmpixel(w http.ResponseWriter, r *http.Request) {
	cookie := crmpixelCookie(r)
	http.SetCookie(w, &cookie)

	//send out pixel 1x1 transparent png file
	pixel, err := crmpixelImage()
	if err == nil {
		w.Write(pixel)
	} else {
		fmt.Fprint(w, "decodig pixel image wrong")
	}
}

func crmpixelCookie(r *http.Request) (ret http.Cookie) {
	cookie, leadok := r.Cookie(cookLeadID) //crm lead

	if leadok != nil {
		ret, _ = createNewCookie(r)
		return
	}

	valid := isValidCookieBiukopCL(cookie.Value)
	if !valid {
		ret, _ = createNewCookie(r)
		return
	}

	//refresh expiration
	ret = *cookie
	ret.Expires = time.Now().Add(10 * 365 * 24 * time.Hour)

	return
}

func getLeadIDFromCookie(r *http.Request) (leadID string, ok bool) {
	ok = false
	cookie, leadok := r.Cookie(cookLeadID) //crm lead
	if leadok != nil {
		return
	}
	valid := isValidCookieBiukopCL(cookie.Value)
	if !valid {
		return
	}

	//correctly split string
	s := strings.Split(cookie.Value, "|")
	if len(s) != 4 || s[0] == "" {
		return
	}
	ok = true
	leadID = s[0]
	return
}

func createNewCookie(r *http.Request) (ret http.Cookie, info crmdLead) {
	info = crmCreateNewAnonymousLeadByHTTPRequest(r)
	ret = createNewCookieByLeadID(info.ID)
	return
}

func createNewCookieByLeadID(leadID string) (ret http.Cookie) {
	expiration := time.Now().Add(10 * 365 * 24 * time.Hour)
	cookieValue := buildBiukopCLValue(leadID)
	ret = http.Cookie{Name: cookLeadID, Value: cookieValue, Expires: expiration}
	return
}

func crmCreateNewAnonymousLeadByHTTPRequest(r *http.Request) (info crmdLead) {
	info.FirstName = "Anonymous"
	info.LastName = "User " + r.RemoteAddr
	info.Password = "Password"
	info.Status = "Anonymous"
	info.Description = "Anonymous user from " + r.RemoteAddr
	info.ForceDuplicate = true
	b, err := json.Marshal(info)
	if err != nil {
		return
	}
	entity, err := crmCreateEntity("Lead", b)
	if err == nil || isErrIndicateDuplicate(err) {
		info, _ = entity.(crmdLead)
	}
	return
}

func isValidCookieBiukopCL(cookieValue string) (valid bool) {
	valid = false

	//correctly split string
	s := strings.Split(cookieValue, "|")
	if len(s) != 4 || s[0] == "" {
		return
	}
	id, nonce, timestamp, signature := s[0], s[1], s[2], s[3]

	//check signature
	combined := id + nonce
	expected := calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete)
	if expected != signature {
		return
	}

	ts, err := strconv.Atoi(timestamp)
	if err != nil {
		return
	}

	if timestampOldThan(int32(ts), 86400) { //older than 1 day
		//find lead data
		_, err := crmpixelLead(id)
		if err != nil {
			return
		}
	}
	valid = true
	return
}

func buildBiukopCLsignature(id, nonce string) (timestamp, signature string) {
	combined := id + nonce
	timestamp = fmt.Sprintf("%d", time.Now().Unix())
	signature = calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete)
	return
}

func buildBiukopCLValue(id string) (ret string) {
	rand.Seed(time.Now().Unix())
	nonce := fmt.Sprintf("%d", rand.Intn(655352017))
	timestamp, signature := buildBiukopCLsignature(id, nonce)
	strs := []string{id, nonce, timestamp, signature} //order is very important
	ret = strings.Join(strs, "|")
	return
}

func crmpixelLead(id string) (info crmdLead, err error) {
	entity, err := crmGetEntity("Lead", id)
	if err != nil {
		return
	}

	info, ok := entity.(crmdLead)
	if !ok {
		err = errors.New("search lead, return a bad entity type")
	}
	return
}

//for user's initial registration, especially for wechat users
//they visit a url that is specifically designed for them to
//auth and input their profile data.
//the url's query string will contains a token and a signature
//so that it's verified, by single get request, to allow people to
//enter their details into the CRM system.
//
//this handler, check's the query sting ,set an auth cookie to the client
//and serve angular app, through an URL "/profile/edit"
//or if the user has already been registered,
//redirect user to a URL "/pages/dashboard"
//
func setTrackingCookieAndRecirect(w http.ResponseWriter, r *http.Request) {

	rq := r.URL.RawQuery
	m, _ := url.ParseQuery(rq)

	//set cookie if any
	leadID, ok := m["lid"]
	if ok {
		log.Println("setlead cookie :" + leadID[0])
		cookie := createNewCookieByLeadID(leadID[0])
		http.SetCookie(w, &cookie)
	} else {
		cookie := crmpixelCookie(r)
		http.SetCookie(w, &cookie)
	}

	url, ok := m["url"]
	if ok {
		http.Redirect(w, r, url[0], 307) //302 temp redirect
		return
	}
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprintf(w, "Not Found URL")
}
