package cmd

import (
	"fmt"
	"image"
	_ "image/png"
	"os"
	"path/filepath"
	"strings"

	"github.com/spf13/cobra"

	"omixlab.com/mosis-portal/pkg/mospkg"
)

// ValidateCmd returns the validate command
func ValidateCmd() *cobra.Command {
	return &cobra.Command{
		Use:   "validate [directory]",
		Short: "Validate project manifest and assets",
		Long:  "Validate a Mosis project without building. Checks manifest.json, assets, and icons.",
		Args:  cobra.MaximumNArgs(1),
		RunE:  runValidate,
	}
}

func runValidate(cmd *cobra.Command, args []string) error {
	dir := "."
	if len(args) > 0 {
		dir = args[0]
	}

	hasErrors := false

	// Validate manifest
	fmt.Println("Validating manifest.json...")
	manifest, errs := validateManifest(dir)
	if len(errs) > 0 {
		hasErrors = true
		for _, e := range errs {
			fmt.Printf("✗ %s\n", e)
		}
	} else {
		fmt.Println("✓ Required fields present")
		fmt.Println("✓ Package ID format valid")
		fmt.Println("✓ Version format valid")
	}

	if manifest == nil {
		return fmt.Errorf("\nValidation failed.")
	}

	// Validate assets
	fmt.Println("\nValidating assets...")
	assetErrs := validateAssets(dir, manifest)
	if len(assetErrs) > 0 {
		hasErrors = true
		for _, e := range assetErrs {
			fmt.Printf("✗ %s\n", e)
		}
	} else {
		fmt.Printf("✓ Entry point exists: %s\n", manifest.Entry)

		// Count files by type
		rmlCount, rcssCount, luaCount := countAssetFiles(dir)
		if rmlCount > 0 {
			fmt.Printf("✓ Found %d RML files\n", rmlCount)
		}
		if rcssCount > 0 {
			fmt.Printf("✓ Found %d RCSS files\n", rcssCount)
		}
		if luaCount > 0 {
			fmt.Printf("✓ Found %d Lua files\n", luaCount)
		}
	}

	// Validate icons
	fmt.Println("\nValidating icons...")
	iconErrs := validateIcons(dir, manifest)
	if len(iconErrs) > 0 {
		hasErrors = true
		for _, e := range iconErrs {
			fmt.Printf("✗ %s\n", e)
		}
	} else {
		if manifest.Icons.Size32 != "" {
			fmt.Printf("✓ icon-32.png\n")
		}
		if manifest.Icons.Size64 != "" {
			fmt.Printf("✓ icon-64.png\n")
		}
		if manifest.Icons.Size128 != "" {
			fmt.Printf("✓ icon-128.png\n")
		}
	}

	// Check permissions
	fmt.Println("\nChecking permissions...")
	if len(manifest.Permissions) == 0 {
		fmt.Println("✓ No permissions declared")
	} else {
		fmt.Printf("✓ Permissions declared: %s\n", strings.Join(manifest.Permissions, ", "))
	}

	if hasErrors {
		return fmt.Errorf("\nValidation failed with errors.")
	}

	fmt.Println("\nAll validations passed!")
	return nil
}

func validateManifest(dir string) (*mospkg.Manifest, []string) {
	manifestPath := filepath.Join(dir, "manifest.json")
	data, err := os.ReadFile(manifestPath)
	if err != nil {
		return nil, []string{"manifest.json not found"}
	}

	manifest, err := mospkg.ParseManifest(data)
	if err != nil {
		return nil, []string{fmt.Sprintf("Invalid manifest.json: %v", err)}
	}

	validationErrs := manifest.Validate()
	var errs []string
	for _, e := range validationErrs {
		errs = append(errs, e.Message)
	}

	if len(errs) > 0 {
		return manifest, errs
	}

	return manifest, nil
}

func validateAssets(dir string, manifest *mospkg.Manifest) []string {
	var errs []string

	// Check entry point exists
	entryPath := filepath.Join(dir, manifest.Entry)
	if _, err := os.Stat(entryPath); os.IsNotExist(err) {
		errs = append(errs, fmt.Sprintf("Entry point not found: %s", manifest.Entry))
	}

	return errs
}

func countAssetFiles(dir string) (rml, rcss, lua int) {
	assetsDir := filepath.Join(dir, "assets")
	filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
		if err != nil || info.IsDir() {
			return nil
		}
		switch strings.ToLower(filepath.Ext(path)) {
		case ".rml":
			rml++
		case ".rcss":
			rcss++
		case ".lua":
			lua++
		}
		return nil
	})
	return
}

func validateIcons(dir string, manifest *mospkg.Manifest) []string {
	var errs []string

	checkIcon := func(path string, expectedSize int) {
		if path == "" {
			return
		}
		fullPath := filepath.Join(dir, path)
		f, err := os.Open(fullPath)
		if err != nil {
			errs = append(errs, fmt.Sprintf("Icon not found: %s", path))
			return
		}
		defer f.Close()

		img, _, err := image.DecodeConfig(f)
		if err != nil {
			errs = append(errs, fmt.Sprintf("Invalid icon format: %s", path))
			return
		}

		if img.Width != expectedSize || img.Height != expectedSize {
			errs = append(errs, fmt.Sprintf("Icon %s should be %dx%d, got %dx%d",
				path, expectedSize, expectedSize, img.Width, img.Height))
		}
	}

	checkIcon(manifest.Icons.Size32, 32)
	checkIcon(manifest.Icons.Size64, 64)
	checkIcon(manifest.Icons.Size128, 128)

	return errs
}
