package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"encoding/csv"
	"strings"
	"log"
	"github.com/joho/godotenv"
	"net/http"
	"time"
	"encoding/json"
	"net/url"
	//"net/http/httputil"
)

type WorkableCandidatesResponse struct {
	Candidates []struct {
		ID        string      `json:"id"`
		Name      string      `json:"name"`
		Firstname string      `json:"firstname"`
		Lastname  string      `json:"lastname"`
		Headline  interface{} `json:"headline"`
		Account   struct {
			Subdomain string `json:"subdomain"`
			Name      string `json:"name"`
		} `json:"account"`
		Job struct {
			Shortcode string `json:"shortcode"`
			Title     string `json:"title"`
		} `json:"job"`
		Stage                  string      `json:"stage"`
		Disqualified           bool        `json:"disqualified"`
		DisqualificationReason interface{} `json:"disqualification_reason"`
		HiredAt                interface{} `json:"hired_at"`
		Sourced                bool        `json:"sourced"`
		ProfileURL             string      `json:"profile_url"`
		Address                interface{} `json:"address"`
		Phone                  interface{} `json:"phone"`
		Email                  string      `json:"email"`
		Domain                 interface{} `json:"domain"`
		CreatedAt              time.Time   `json:"created_at"`
		UpdatedAt              time.Time   `json:"updated_at"`
	} `json:"candidates"`
	Paging struct {
		Next string `json:"next"`
	} `json:"paging"`
}

func main() {
	loadEnv()

	devMapsFromLinkedIn := loadLinkedInDevs()
	candidatesInWorkable := getWorkableCandidates(devMapsFromLinkedIn)
	devsFromLinkedInSlice := convertDevsFromLinkedInToSlice(devMapsFromLinkedIn)
	devsNotInWorkable := findDevsInLinkedInButNotWorkable(devsFromLinkedInSlice, candidatesInWorkable)

	writeToFile("workableCandidates.csv", candidatesInWorkable)
	writeToFile("devsInLinkedInButNotInWorkable.csv", devsNotInWorkable)
	writeToFile("devsFromLinkedIn.csv", devsFromLinkedInSlice)

	fmt.Println(len(devMapsFromLinkedIn))
	fmt.Println(len(devsNotInWorkable))
	fmt.Println(len(candidatesInWorkable))
}

func convertDevsFromLinkedInToSlice(devsFromLinkedInMaps []map[string]string) [][]string {
	data := make([][]string, 0)
	data = append(data, []string{"First Name", "Last Name", "Email", "Company", "Position"})

	for _, dev := range devsFromLinkedInMaps {
		data = append(data, []string{dev["first_name"], dev["last_name"], dev["email"], dev["company"], dev["position"]})
	}

	return data
}

func getWorkableCandidates(devsFromLinkedIn []map[string]string) [][]string {
	candidatesInWorkable := make([][]string, 0)
	candidatesInWorkable = append(candidatesInWorkable, []string{"First Name", "Last Name", "Email", "Company", "Position"})
	fmt.Println(len(devsFromLinkedIn))
	candidatesInWorkable = getCandidates(os.Getenv("WORKABLE_URL")+"/candidates", devsFromLinkedIn, candidatesInWorkable)
	fmt.Println(len(candidatesInWorkable))
	return candidatesInWorkable
}

func loadLinkedInDevs() []map[string]string {
	file := loadLinkedInConnections()
	defer file.Close()
	reader := csv.NewReader(file)
	firstNameIndex := 0
	lastNameIndex := 1
	emailIndex := 2
	companyIndex := 3
	positionIndex := 4
	//connectedOnIndex := 5
	//tagsIndex := 6
	devs := make([]map[string]string, 0)
	for {
		record, err := reader.Read()

		if err == io.EOF {
			break
		}

		checkError("error reading file", err)

		if false == hasPosition(record[positionIndex]) {
			continue
		}

		person := map[string]string{
			"first_name": record[firstNameIndex],
			"last_name":  record[lastNameIndex],
			"email":      record[emailIndex],
			"company":    record[companyIndex],
			"position":   record[positionIndex],
		}

		devs = append(devs, person)
	}
	return devs
}

func loadLinkedInConnections() *os.File {
	file, err := os.Open("data/Connections.csv")
	checkError("Failed to open connections file", err)

	return file
}

func loadEnv() {
	err := godotenv.Load()
	checkError("ENV failed to load", err)
}

func printError(err error) (n int, error error) {
	return fmt.Println("Error: ", err)
}

func hasPosition(testPosition string) bool {
	positions := [7]string{"dev", "developer", "engineer", "programmer", "code", "consultant", "freelance"}

	for _, position := range positions {
		containsPosition := strings.Contains(strings.ToLower(testPosition), position)

		if containsPosition == true {
			return true
		}
	}

	return false
}

func checkError(message string, err error) {
	if err != nil {
		log.Fatal(message, err)
		os.Exit(1)
	}
}

func getCandidates(url string, devs []map[string]string, workableCandidates [][]string) [][]string {
	fmt.Println("Calling: " + url)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Authorization", "Bearer " + os.Getenv("WORKABLE_API_KEY"))
	req.Header.Add("Content-Type", "application/json")
	resp, err := client.Do(req)

	//dump, err := httputil.DumpResponse(resp, true)
	//if err != nil {
	//	log.Fatal(err)
	//}
	//
	//fmt.Printf("%q", dump)

	checkError("error making request", err)

	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	checkError("couldn't read response", err)

	var candidates WorkableCandidatesResponse
	err = json.Unmarshal(body, &candidates)
	checkError("couldn't unmarshall response", err)

	for _, candidate := range candidates.Candidates {
		for _, dev := range devs {
			if strings.ToLower(dev["first_name"]) == strings.ToLower(candidate.Firstname) && strings.ToLower(dev["last_name"]) == strings.ToLower(candidate.Lastname) {
				workableCandidates = append(workableCandidates, []string{dev["first_name"], dev["last_name"], dev["email"], dev["company"], dev["position"]})
			}
		}
	}

	if isValidUrl(candidates.Paging.Next) {
		fmt.Println("Calling getCandidates again")
		return getCandidates(candidates.Paging.Next, devs, workableCandidates)
	}

	return workableCandidates
}

func writeToFile(fileName string, data [][]string) bool {
	file, err := os.Create("data/" +  fileName)
	checkError("Cannot create file", err)
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	for _, value := range data {
		err := writer.Write(value)
		checkError("Cannot write to file", err)
	}

	return true
}

func isValidUrl(toTest string) bool {
	_, err := url.ParseRequestURI(toTest)
	if err != nil {
		return false
	} else {
		return true
	}
}

func findDevsInLinkedInButNotWorkable(linkedInDevs [][]string, workableCandidates [][]string) [][]string {
	out := make([][]string, 0)
	for _, dev := range linkedInDevs {
		found := false

		for _, candidate := range workableCandidates {
			firstNameMatch := strings.ToLower(dev[0]) == strings.ToLower(candidate[0])
			lastNameMatch := strings.ToLower(dev[1]) == strings.ToLower(candidate[1])
			if firstNameMatch && lastNameMatch {
				found = true
			}
		}

		if found == false {
			out = append(out, dev)
		}
	}

	return out
}