// Package sdk provides OpenAPI SDK generation and diff capabilities.
package sdk

import (
	"context"
	"fmt"
	"path/filepath"

	"github.com/host-uk/core/internal/cmd/sdk/generators"
)

// Config holds SDK generation configuration from .core/release.yaml.
type Config struct {
	// Spec is the path to the OpenAPI spec file (auto-detected if empty).
	Spec string `yaml:"spec,omitempty"`
	// Languages to generate SDKs for.
	Languages []string `yaml:"languages,omitempty"`
	// Output directory (default: sdk/).
	Output string `yaml:"output,omitempty"`
	// Package naming configuration.
	Package PackageConfig `yaml:"package,omitempty"`
	// Diff configuration for breaking change detection.
	Diff DiffConfig `yaml:"diff,omitempty"`
	// Publish configuration for monorepo publishing.
	Publish PublishConfig `yaml:"publish,omitempty"`
}

// PackageConfig holds package naming configuration.
type PackageConfig struct {
	// Name is the base package name.
	Name string `yaml:"name,omitempty"`
	// Version is the SDK version (supports templates like {{.Version}}).
	Version string `yaml:"version,omitempty"`
}

// DiffConfig holds breaking change detection configuration.
type DiffConfig struct {
	// Enabled determines whether to run diff checks.
	Enabled bool `yaml:"enabled,omitempty"`
	// FailOnBreaking fails the release if breaking changes are detected.
	FailOnBreaking bool `yaml:"fail_on_breaking,omitempty"`
}

// PublishConfig holds monorepo publishing configuration.
type PublishConfig struct {
	// Repo is the SDK monorepo (e.g., "myorg/sdks").
	Repo string `yaml:"repo,omitempty"`
	// Path is the subdirectory for this SDK (e.g., "packages/myapi").
	Path string `yaml:"path,omitempty"`
}

// SDK orchestrates OpenAPI SDK generation.
type SDK struct {
	config     *Config
	projectDir string
	version    string
}

// New creates a new SDK instance.
func New(projectDir string, config *Config) *SDK {
	if config == nil {
		config = DefaultConfig()
	}
	return &SDK{
		config:     config,
		projectDir: projectDir,
	}
}

// SetVersion sets the SDK version for generation.
// This updates both the internal version field and the config's Package.Version.
func (s *SDK) SetVersion(version string) {
	s.version = version
	if s.config != nil {
		s.config.Package.Version = version
	}
}

// DefaultConfig returns sensible defaults for SDK configuration.
func DefaultConfig() *Config {
	return &Config{
		Languages: []string{"typescript", "python", "go", "php"},
		Output:    "sdk",
		Diff: DiffConfig{
			Enabled:        true,
			FailOnBreaking: false,
		},
	}
}

// Generate generates SDKs for all configured languages.
func (s *SDK) Generate(ctx context.Context) error {
	// Generate for each language
	for _, lang := range s.config.Languages {
		if err := s.GenerateLanguage(ctx, lang); err != nil {
			return err
		}
	}

	return nil
}

// GenerateLanguage generates SDK for a specific language.
func (s *SDK) GenerateLanguage(ctx context.Context, lang string) error {
	specPath, err := s.DetectSpec()
	if err != nil {
		return err
	}

	registry := generators.NewRegistry()
	registry.Register(generators.NewTypeScriptGenerator())
	registry.Register(generators.NewPythonGenerator())
	registry.Register(generators.NewGoGenerator())
	registry.Register(generators.NewPHPGenerator())

	gen, ok := registry.Get(lang)
	if !ok {
		return fmt.Errorf("sdk.GenerateLanguage: unknown language: %s", lang)
	}

	if !gen.Available() {
		fmt.Printf("Warning: %s generator not available. Install with: %s\n", lang, gen.Install())
		fmt.Printf("Falling back to Docker...\n")
	}

	outputDir := filepath.Join(s.projectDir, s.config.Output, lang)
	opts := generators.Options{
		SpecPath:    specPath,
		OutputDir:   outputDir,
		PackageName: s.config.Package.Name,
		Version:     s.config.Package.Version,
	}

	fmt.Printf("Generating %s SDK...\n", lang)
	if err := gen.Generate(ctx, opts); err != nil {
		return fmt.Errorf("sdk.GenerateLanguage: %s generation failed: %w", lang, err)
	}
	fmt.Printf("Generated %s SDK at %s\n", lang, outputDir)

	return nil
}
