func main() {

	VALID_PARAMETERS := []string{
		"from", "to", "types", "limit",
	}

	app := cli.NewApp()

	app.Version = "0.0.1"
	app.Name = "suppression-sparkpost-cli"
	app.Usage = "SparkPost suppression list cli"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "baseurl, u",
			Value:  "https://api.sparkpost.com",
			Usage:  "Optional baseUrl for SparkPost.",
			EnvVar: "SPARKPOST_BASEURL",
		},
		cli.StringFlag{
			Name:   "apikey, k",
			Value:  "",
			Usage:  "Required SparkPost API key",
			EnvVar: "SPARKPOST_API_KEY",
		},
		cli.StringFlag{
			Name:  "verbose",
			Value: "false",
			Usage: "Dumps additional information to console",
		},
		cli.StringFlag{
			Name:  "file, f",
			Value: "",
			Usage: "Mandrill blocklist CSV. See https://mandrill.zendesk.com/hc/en-us/articles/205582997",
		},
		cli.StringFlag{
			Name:  "command",
			Value: "list",
			Usage: "Optional one of list, retrieve, search, delete, mandrill, sendgrid",
		},
		cli.StringFlag{
			Name:  "recipient",
			Value: "",
			Usage: "Recipient email address. Example [email protected]",
		},

		// Search Parameters
		cli.StringFlag{
			Name:  "from",
			Value: "",
			Usage: "Optional datetime the entries were last updated, in the format of YYYY-MM-DDTHH:mm:ssZ (2015-04-10T00:00:00)",
		},
		cli.StringFlag{
			Name:  "to",
			Value: "",
			Usage: "Optional datetime the entries were last updated, in the format YYYY-MM-DDTHH:mm:ssZ (2015-04-10T00:00:00)",
		},
		cli.StringFlag{
			Name:  "types",
			Value: "",
			Usage: "Optional types of entries to include in the search, i.e. entries with \"transactional\" and/or \"non_transactional\" keys set to true",
		},
		cli.StringFlag{
			Name:  "limit",
			Value: "",
			Usage: "Optional maximum number of results to return. Must be between 1 and 100000. Default value is 100000",
		},
	}
	app.Action = func(c *cli.Context) {

		if c.String("apikey") == "" {
			log.Fatalf("Error: SparkPost API key must be set\n")
			return
		}

		isVerbose := false
		if c.String("verbose") == "true" {
			isVerbose = true
		}

		cfg := &sp.Config{
			BaseUrl:    c.String("baseurl"),
			ApiKey:     c.String("apikey"),
			ApiVersion: 1,
			Verbose:    isVerbose,
		}

		var client sp.Client
		err := client.Init(cfg)
		if err != nil {
			log.Fatalf("SparkPost client init failed: %s\n", err)
			return
		}

		parameters := make(map[string]string)

		for i, val := range VALID_PARAMETERS {

			if c.String(VALID_PARAMETERS[i]) != "" {
				parameters[val] = c.String(val)
			}
		}

		switch c.String("command") {
		case "list":
			e, err := client.SuppressionList()

			if err != nil {
				log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
				return
			} else {
				csvEntryPrinter(e, true)

			}
		case "retrieve":
			recpipient := c.String("recipient")
			if recpipient == "" {
				log.Fatalf("ERROR: The `retrieve` command requires a recipient.")
				return
			}

			e, err := client.SuppressionRetrieve(recpipient)

			if err != nil {
				log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
				return
			} else {
				csvEntryPrinter(e, false)

			}
		case "search":
			parameters := make(map[string]string)

			for i, val := range VALID_PARAMETERS {

				if c.String(VALID_PARAMETERS[i]) != "" {
					parameters[val] = c.String(val)
				}
			}

			e, err := client.SuppressionSearch(parameters)

			if err != nil {
				log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
				return
			} else {
				csvEntryPrinter(e, true)

			}
		case "delete":
			recpipient := c.String("recipient")
			if recpipient == "" {
				log.Fatalf("ERROR: The `delete` command requires a recipient.")
				return
			}

			_, err := client.SuppressionDelete(recpipient)

			if err != nil {
				log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
				return
			} else {
				fmt.Println("OK")

			}
		case "mandrill":
			fmt.Printf("Processing: %s\n", c.String("file"))
			file := c.String("file")
			if file == "" {
				log.Fatalf("ERROR: The `mandrill` command requires a CSV file.")
				return
			}

			f, err := os.Open(file)
			check(err)

			var entries = []sp.SuppressionEntry{}

			batchCount := 1

			blackListRow := csv.NewReader(bufio.NewReader(f))
			blackListRow.FieldsPerRecord = 8

			for {
				record, err := blackListRow.Read()
				if err == io.EOF {
					break
				}

				if err != nil {
					log.Fatalf("ERROR: Failed to process '%s':\n\t%s", file, err)

					return
				}

				if record[MANDRILL_EMAIL_COL] == "email" {
					// Skip over header row
					continue
				}

				if record[MANDRILL_REASON_COL] != "hard-bounce" {
					// Ignore soft-bounce
					continue
				}

				if strings.Count(record[MANDRILL_EMAIL_COL], "@") != 1 {
					fmt.Printf("WARN: Ignoring '%s'. It is not a valid email address.\n", record[MANDRILL_EMAIL_COL])
					continue
				}

				entry := sp.SuppressionEntry{}

				if record[MANDRILL_EMAIL_COL] == "" {
					// Must have email as it is suppression list primary key
					continue
				}

				entry.Email = record[MANDRILL_EMAIL_COL]
				entry.Transactional = false
				entry.NonTransactional = true
				entry.Description = fmt.Sprintf("MBL: %s", record[MANDRILL_DETAIL_COL])

				entries = append(entries, entry)

				if len(entries) > (1024 * 100) {
					fmt.Printf("Uploading batch %d\n", batchCount)
					err = client.SuppressionInsertOrUpdate(entries)

					if err != nil {
						log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
						return
					}
					entries = []sp.SuppressionEntry{}
					batchCount++
				}
			}

			if len(entries) > 0 {
				fmt.Printf("Uploading batch %d\n", batchCount)
				err = client.SuppressionInsertOrUpdate(entries)

				if err != nil {
					log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
					return
				}
			}
			fmt.Println("DONE")

		case "sendgrid":
			file := c.String("file")
			if file == "" {
				log.Fatalf("ERROR: The `sendgrid` command requires a CSV file.")
				return
			}

			f, err := os.Open(file)
			check(err)

			var entries = []sp.SuppressionEntry{}

			batchCount := 1

			blackListRow := csv.NewReader(bufio.NewReader(f))
			blackListRow.FieldsPerRecord = 2

			for {
				record, err := blackListRow.Read()
				if err == io.EOF {
					break
				}

				if err != nil {
					log.Fatalf("ERROR: Failed to process '%s':\n\t%s", file, err)

					return
				}

				if record[SENDGRID_EMAIL_COL] == "email" {
					// Skip over header row
					continue
				}

				entry := sp.SuppressionEntry{}

				if record[SENDGRID_EMAIL_COL] == "" {
					// Must have email as it is suppression list primary key
					continue
				}

				// SendGrid suppression lists are very dirty and tend to have invalid data. Some examples of invalid addresses are:
				// 	#02232014, gmail.com, To, 8/27/2015, [email protected]@domain.com"
				if strings.Count(record[MANDRILL_EMAIL_COL], "@") != 1 {
					fmt.Printf("WARN: Ignoring '%s'. It is not a valid email address.\n", record[MANDRILL_EMAIL_COL])
					continue
				}

				entry.Email = record[SENDGRID_EMAIL_COL]
				entry.Transactional = false
				entry.NonTransactional = true
				entry.Description = fmt.Sprintf("SBL: imported from SendGrid")

				entries = append(entries, entry)

				if len(entries) > (1024 * 100) {
					fmt.Printf("Uploading batch %d\n", batchCount)
					err = client.SuppressionInsertOrUpdate(entries)

					if err != nil {
						log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
						return
					}
					entries = []sp.SuppressionEntry{}
					batchCount++
				}

			}

			if len(entries) > 0 {
				fmt.Printf("Uploading batch %d\n", batchCount)
				err = client.SuppressionInsertOrUpdate(entries)

				if err != nil {
					log.Fatalf("ERROR: %s\n\nFor additional information try using `--verbose true`\n\n\n", err)
					return
				}
			}
			fmt.Println("DONE")

		default:
			fmt.Printf("\n\nERROR: Unknown Commnad[%s]\n\n", c.String("command"))

			return
		}

	}
	app.Run(os.Args)

}