package main

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/spf13/cobra"
)

var (
	_ = flagVersion // used in runDownload
	_ = flagRepo
	_ = flagForce
)

func downloadCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "download",
		Short: "Download schema.json and meta.json from GitHub",
		RunE:  runDownload,
	}
	cmd.Flags().StringVarP(&flagVersion, "version", "v", "", "Git tag or branch (e.g., v1.0.0 or main)")
	cmd.Flags().StringVar(&flagRepo, "repo", "agentclientprotocol/agent-client-protocol", "GitHub repository")
	cmd.Flags().StringVarP(&flagOutput, "output", "o", "./schema", "Output directory")
	cmd.Flags().BoolVar(&flagForce, "force", false, "Overwrite existing files")
	return cmd
}

func runDownload(cmd *cobra.Command, args []string) error {
	ref := resolveRef(flagVersion)
	repo := flagRepo

	outDir := flagOutput
	if err := os.MkdirAll(outDir, 0755); err != nil {
		return fmt.Errorf("create output dir: %w", err)
	}

	schemaPath := filepath.Join(outDir, "schema.json")
	metaPath := filepath.Join(outDir, "meta.json")
	versionPath := filepath.Join(outDir, "VERSION")

	if !flagForce {
		if existing, err := os.ReadFile(versionPath); err == nil {
			if strings.TrimSpace(string(existing)) == ref {
				fmt.Printf("Already at version %s (use --force to re-download)\n", ref)
				return nil
			}
		}
	}

	// Download schema.json (prefer stable, fall back to unstable)
	schemaDownloaded, schemaURL, err := downloadGitHubFile(repo, ref, "schema/schema.json", schemaPath)
	if err != nil {
		// Fall back to unstable
		schemaDownloaded, schemaURL, err = downloadGitHubFile(repo, ref, "schema/schema.unstable.json", schemaPath)
		if err != nil {
			return fmt.Errorf("download schema (tried stable and unstable): %w", err)
		}
		fmt.Printf("Downloaded unstable schema from %s\n", schemaURL)
	} else {
		fmt.Printf("Downloaded schema from %s\n", schemaURL)
	}
	_ = schemaDownloaded

	// Download meta.json (prefer stable, fall back to unstable)
	metaDownloaded, metaURL, err := downloadGitHubFile(repo, ref, "schema/meta.json", metaPath)
	if err != nil {
		metaDownloaded, metaURL, err = downloadGitHubFile(repo, ref, "schema/meta.unstable.json", metaPath)
		if err != nil {
			return fmt.Errorf("download meta (tried stable and unstable): %w", err)
		}
		fmt.Printf("Downloaded unstable meta from %s\n", metaURL)
	} else {
		fmt.Printf("Downloaded meta from %s\n", metaURL)
	}
	_ = metaDownloaded

	if err := os.WriteFile(versionPath, []byte(ref+"\n"), 0644); err != nil {
		return fmt.Errorf("write VERSION: %w", err)
	}

	// Validate JSON files
	if err := validateJSON(schemaPath); err != nil {
		return fmt.Errorf("invalid schema.json: %w", err)
	}
	if err := validateJSON(metaPath); err != nil {
		return fmt.Errorf("invalid meta.json: %w", err)
	}

	fmt.Printf("Schema updated to %s\n", ref)
	return nil
}

func resolveRef(version string) string {
	if version == "" {
		return "main"
	}
	if version == "main" || version == "master" {
		return version
	}
	if regexp.MustCompile(`^v?\d+\.\d+\.\d+$`).MatchString(version) {
		if !strings.HasPrefix(version, "v") {
			version = "v" + version
		}
		return version
	}
	if strings.HasPrefix(version, "refs/") {
		// Strip refs/tags/ or refs/heads/
		parts := strings.SplitN(version, "/", 3)
		return parts[len(parts)-1]
	}
	return version
}

// downloadGitHubFile downloads a file from GitHub using the Contents API.
func downloadGitHubFile(repo, ref, path, destPath string) (bool, string, error) {
	apiURL := fmt.Sprintf("https://api.github.com/repos/%s/contents/%s?ref=%s", repo, path, ref)
	resp, err := http.Get(apiURL)
	if err != nil {
		return false, "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		return false, "", fmt.Errorf("file not found: %s", path)
	}
	if resp.StatusCode != 200 {
		return false, "", fmt.Errorf("HTTP %d", resp.StatusCode)
	}

	var entry struct {
		Content string `json:"content"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&entry); err != nil {
		return false, "", err
	}

	// GitHub API returns base64-encoded content with line breaks
	decoded, err := base64.StdEncoding.DecodeString(strings.ReplaceAll(entry.Content, "\n", ""))
	if err != nil {
		return false, "", fmt.Errorf("decode base64: %w", err)
	}

	if err := os.WriteFile(destPath, decoded, 0644); err != nil {
		return false, "", err
	}

	return true, apiURL, nil
}

func validateJSON(p string) error {
	data, err := os.ReadFile(p)
	if err != nil {
		return err
	}
	var v any
	return json.Unmarshal(data, &v)
}

// --- unused but kept for potential future use ---
func downloadFile(urlStr, destPath string) error {
	resp, err := http.Get(urlStr)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return fmt.Errorf("HTTP %d", resp.StatusCode)
	}
	f, err := os.Create(destPath)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, resp.Body)
	return err
}

func parseURL(rawURL string) (*url.URL, error) {
	return url.Parse(rawURL)
}
