package main

import (
	"encoding/csv"
	"errors"
	"fmt"
	"os"
	"reflect"
	"strconv"
)

type Gender rune

const (
	Male   Gender = 'M'
	Female Gender = 'F'
)

type Quarter string

const (
	QuarterOne   Quarter = "Q1"
	QuarterTwo   Quarter = "Q2"
	QuarterThree Quarter = "Q3"
	QuarterFour  Quarter = "Q4"
)

type Half string

const (
	HalfOne Half = "H1"
	HalfTwo Half = "H2"
)

type Month string

const (
	January   Month = "January"
	February  Month = "February"
	March     Month = "March"
	April     Month = "April"
	May       Month = "May"
	June      Month = "June"
	July      Month = "July"
	August    Month = "August"
	September Month = "September"
	October   Month = "October"
	November  Month = "November"
	December  Month = "December"
)

type MonthShort string

const (
	Jan      MonthShort = "Jan"
	Feb      MonthShort = "Feb"
	Mar      MonthShort = "Mar"
	Apr      MonthShort = "Apr"
	MayShort MonthShort = "May"
	Jun      MonthShort = "Jun"
	Jul      MonthShort = "Jul"
	Aug      MonthShort = "Aug"
	Sep      MonthShort = "Sep"
	Oct      MonthShort = "Oct"
	Nov      MonthShort = "Nov"
	Dec      MonthShort = "Dec"
)

type WeekDay string

const (
	Monday    WeekDay = "Monday"
	Tuesday   WeekDay = "Tuesday"
	Wednesday WeekDay = "Wednesday"
	Thursday  WeekDay = "Thursday"
	Friday    WeekDay = "Friday"
	Saturday  WeekDay = "Saturday"
	Sunday    WeekDay = "Sunday"
)

type WeekDayShort string

const (
	Mon WeekDayShort = "Mon"
	Tue WeekDayShort = "Tue"
	Wed WeekDayShort = "Wed"
	Thu WeekDayShort = "Thu"
	Fri WeekDayShort = "Fri"
	Sat WeekDayShort = "Sat"
	Sun WeekDayShort = "Sun"
)

type Employee struct {
	EmpID                 uint         `csv:"Emp ID"`
	NamePrefix            string       `csv:"Name Prefix"`
	FirstName             string       `csv:"First Name"`
	MiddleInitial         string       `csv:"Middle Initial"`
	LastName              string       `csv:"Last Name"`
	Gender                Gender       `csv:"Gender"`
	Email                 string       `csv:"E Mail"`
	FathersName           string       `csv:"Father's Name"`
	MothersName           string       `csv:"Mothers's Name"`
	MothersMaidenName     string       `csv:"Mother's Maiden Name"`
	Birthdate             string       `csv:"Date of Birth"`
	BirthTime             string       `csv:"Time of Birth"`
	WeightKg              uint16       `csv:"Weight in Kgs."`
	JoiningDate           string       `csv:"Date of Joining"`
	JoiningQuarter        Quarter      `csv:"Quarter of Joining"`
	JoiningHalf           Half         `csv:"Half of Joining"`
	JoiningYear           uint16       `csv:"Year of Joining"`
	JoiningMonth          uint8        `csv:"Month of Joining"`
	JoiningMonthName      Month        `csv:"Month Name of Joining"`
	JoiningMonthNameShort MonthShort   `csv:"Short Month"`
	JoiningMonthDay       uint8        `csv:"Day of Joining"`
	JoiningWeekDay        WeekDay      `csv:"DOW of Joining"`
	JoiningWeekDayShort   WeekDayShort `csv:"Short DOW"`
	YearsOfService        float32      `csv:"Age in Company (Years)"`
	Salary                uint32       `csv:"Salary"`
	LatestHikePercentage  string       `csv:"Last % Hike"`
	SSN                   string       `csv:"SSN"`
	PhoneNumber           string       `csv:"Phone No."`
	PlaceName             string       `csv:"Place Name"`
	County                string       `csv:"County"`
	City                  string       `csv:"City"`
	State                 string       `csv:"State"`
	Zip                   uint32       `csv:"Zip"`
	Username              string       `csv:"User Name"`
	Password              string       `csv:"Password"`
	// Empty slot
	// Empty slot
	// Empty slot
}

func UnmarshallEmployees(reader *csv.Reader) ([]Employee, error) {
	reader.Comma = ','
	headers, err := reader.Read()
	if err != nil {
		return nil, errors.New("Couldn't read the headers.")
	}

	fieldMap := make(map[string]int)
	for i, header := range headers {
		fieldMap[header] = i
	}

	fmt.Println(fieldMap)

	var employees []Employee
	for {
		record, err := reader.Read()
		if err != nil {
			break
		}

		employee := Employee{}
    reflection := reflect.ValueOf(&employee).Elem()
    // fmt.Println(reflection.FieldByName("Emp ID"))
		for fieldName, index := range fieldMap {
			field := reflection.FieldByName(fieldName)
      fmt.Println(reflection.Field(2).Type())
			if field.IsValid() {
        // fmt.Println("Field type: ", field.Type().Name(), fieldName)
				switch field.Type().Name() {
				case "string":
					field.SetString(record[index])
				case "uint":
					if intValue, err := strconv.ParseUint(record[index], 10, 64); err == nil {
						field.SetUint(intValue)
					}
				case "uint8":
					if intValue, err := strconv.ParseUint(record[index], 10, 8); err == nil {
						field.SetUint(intValue)
					}
				case "uint16":
					if intValue, err := strconv.ParseUint(record[index], 10, 16); err == nil {
						field.SetUint(intValue)
					}
				case "uint32":
					if intValue, err := strconv.ParseUint(record[index], 10, 32); err == nil {
						field.SetUint(intValue)
					}
				case "float32":
					if floatValue, err := strconv.ParseFloat(record[index], 32); err == nil {
						field.SetFloat(floatValue)
					}
				case "Gender":
					field.Set(reflect.ValueOf(Gender(rune(record[index][0]))))
				case "Quarter":
					field.Set(reflect.ValueOf(Quarter(string(record[index]))))
				case "Half":
					field.Set(reflect.ValueOf(Half(string(record[index]))))
				case "Month":
					field.Set(reflect.ValueOf(Month(string(record[index]))))
				case "MonthShort":
					field.Set(reflect.ValueOf(MonthShort(string(record[index]))))
				case "WeekDay":
					field.Set(reflect.ValueOf(WeekDay(string(record[index]))))
				case "WeekDayShort":
					field.Set(reflect.ValueOf(WeekDayShort(string(record[index]))))
				}
			} else {
        // fmt.Println(fieldName)
      }

		}
		employees = append(employees, employee)
	}

	return employees, nil
}

func main() {
	f, err := os.Open("50000 Records.csv")
	if err != nil {
		fmt.Println(err)
	}

	defer f.Close()

	reader := csv.NewReader(f)

  employees, err := UnmarshallEmployees(reader)
	if err != nil {
		fmt.Println(err)
	}
	// pType := reflect.ValueOf(Employee{})
 //  fmt.Println(pType.)

	// // Iterate over the fields of the struct
	// for i := 0; i < pType.NumField(); i++ {
	// 	field := pType.Field(i)
	// 	fmt.Printf("Field: %s, CSV Tag: %s\n", field.Name, field.Tag.Get("csv"))
	// }

  fmt.Println(employees[:10])
	// for _, record := range records {
	//   fmt.Println(record)
	// }

}
