package swagger

import (
	"encoding/json"
	"errors"
	"os"
	"path/filepath"
	"strings"

	"go-doc/internal/util"

	"github.com/spf13/cobra"
	"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/parser"
	"gopkg.in/yaml.v2"
)

var (
	// VarStringAPI specifies the API filename.
	VarStringAPI string

	// VarStringDir specifies the directory to generate swagger file.
	VarStringDir string

	// VarStringFilename specifies the generated swagger file name without the extension.
	VarStringFilename string

	// VarBoolYaml specifies whether to generate a YAML file.
	VarBoolYaml bool

	// VarStringSpecVersion specifies the OpenAPI specification version (swagger2.0 or openapi3.0).
	VarStringSpecVersion string
)

func Command(_ *cobra.Command, _ []string) error {
	if len(VarStringAPI) == 0 {
		return errors.New("missing -api")
	}

	if len(VarStringDir) == 0 {
		return errors.New("missing -dir")
	}

	// Validate spec version
	if VarStringSpecVersion != "swagger2.0" && VarStringSpecVersion != "openapi3.0" {
		return errors.New("spec-version must be either 'swagger2.0' or 'openapi3.0'")
	}

	api, err := parser.Parse(VarStringAPI, "")
	if err != nil {
		return err
	}

	fillAllStructs(api)

	if err := api.Validate(); err != nil {
		return err
	}

	var data []byte

	if VarStringSpecVersion == "openapi3.0" {
		// Generate OpenAPI 3.0
		swagger2, err := spec2Swagger(api)
		if err != nil {
			return err
		}
		openapi3Doc := convertSwagger2ToOpenAPI3(swagger2)
		data, err = json.MarshalIndent(openapi3Doc, "", "  ")
		if err != nil {
			return err
		}
	} else {
		// Generate Swagger 2.0 (default)
		swagger, err := spec2Swagger(api)
		if err != nil {
			return err
		}
		data, err = json.MarshalIndent(swagger, "", "  ")
		if err != nil {
			return err
		}
	}

	err = util.MkdirIfNotExist(VarStringDir)
	if err != nil {
		return err
	}

	filename := VarStringFilename
	if filename == "" {
		base := filepath.Base(VarStringAPI)
		filename = strings.TrimSuffix(base, filepath.Ext(base))
	}

	if VarBoolYaml {
		filePath := filepath.Join(VarStringDir, filename+".yaml")

		var jsonObj interface{}
		if err := yaml.Unmarshal(data, &jsonObj); err != nil {
			return err
		}

		data, err := yaml.Marshal(jsonObj)
		if err != nil {
			return err
		}
		return os.WriteFile(filePath, data, 0644)
	}

	// generate json file
	filePath := filepath.Join(VarStringDir, filename+".json")
	return os.WriteFile(filePath, data, 0644)
}
