package main

import (
	"errors"
	"fmt"
	"net/url"
	"os"

	"code.justin.tv/spade/code-generator/internal"
	"code.justin.tv/spade/code-generator/internal/typescript"
	"github.com/fatih/color"
	"github.com/jpillora/opts"
)

type Config struct {
	Version  `opts:"mode=cmd,help=print the version spade-code-generator"`
	Generate `opts:"mode=cmd,help=generate event definitions from the spade schema registry"`
}

func main() {
	err := opts.Parse(&Config{}).Run()
	if err != nil {
		color.Red(err.Error())
		os.Exit(1)
	}
}

type Version struct{}

func (f *Version) Run() error {
	fmt.Println(internal.Version)
	return nil
}

type Generate struct {
	Language                        string `opts:"mode=flag,help=the ouput language"`
	Out                             string `opts:"mode=flag,help=the ouput directory"`
	Source                          string `opts:"mode=flag,help=the url or path to a spade schema"`
	GuardExpectationsWithExpression string `opts:"mode=flag,help=an expression used for dead-code elimination in production build"`
}

func (f *Generate) Run() error {
	if err := validate(f); err != nil {
		return err
	}

	var err error
	var schema *internal.Schema

	switch {
	case isValidUrl(f.Source):
		client, err := internal.NewMidwayClient()
		if err != nil {
			return err
		}

		schema, err = internal.GetYamlSchemaFromURL(client, f.Source)
		if err != nil {
			return err
		}
	default:
		schema, err = internal.GetYamlSchemaFromDisk(f.Source)
		if err != nil {
			return err
		}
	}

	switch f.Language {
	case "typescript":
		err = typescript.GenerateCodeToFile(*schema, f.Out, typescript.GenerationOptions{
			GuardExpectationsWithExpression: f.GuardExpectationsWithExpression,
		})
	default:
		return errors.New("language is invalid, supported languages are 'typescript'")
	}

	return err
}

func validate(f *Generate) error {
	if f.Out == "" {
		return errors.New("--ouput is a required argument")
	}

	fi, err := os.Stat(f.Out)

	if os.IsNotExist(err) {
		return errors.New("ouput directory does not exist, please create first")
	}

	if !fi.IsDir() {
		return errors.New("ouput directory is not a directory, please point to a valid directory")
	}

	if f.GuardExpectationsWithExpression == "" {
		f.GuardExpectationsWithExpression = "true"
	}

	return nil
}

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

	u, err := url.Parse(t)
	if err != nil || u.Scheme == "" || u.Host == "" {
		return false
	}

	return true
}
