package cmd

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	gogitea "code.gitea.io/sdk/gitea"
	"github.com/spf13/cobra"
)

var (
	npType    string
	npPrivate bool
)

var newprojectCmd = &cobra.Command{
	Use:   "newproject <name>",
	Short: "Full project scaffold: local + Gitea + beepc clone + homelab service + Caddy subdomain",
	Args:  cobra.ExactArgs(1),
	RunE:  runNewProject,
}

func init() {
	newprojectCmd.Flags().StringVarP(&npType, "type", "t", "", "Stack type: node, python, go, static (required)")
	newprojectCmd.Flags().BoolVarP(&npPrivate, "private", "p", true, "Make the Gitea repo private")
	newprojectCmd.Flags().StringVarP(&repoDesc, "desc", "d", "", "Repo description")
	rootCmd.AddCommand(newprojectCmd)
}

func runNewProject(cmd *cobra.Command, args []string) error {
	name := args[0]

	if npType == "" {
		return fmt.Errorf("--type/-t is required: node, python, go, static")
	}
	switch npType {
	case "node", "python", "go", "static":
	default:
		return fmt.Errorf("unknown type %q — valid: node, python, go, static", npType)
	}

	cfg, err := loadConfig()
	if err != nil {
		return err
	}
	if cfg.SSHHost == "" || cfg.SSHUser == "" {
		return fmt.Errorf("homelab SSH not configured — run 'gtea config' to set ssh_host and ssh_user")
	}

	step := func(n int, msg string) { fmt.Printf("[%d/6] %s\n", n, msg) }
	fmt.Printf("==> Scaffolding %q (%s)\n\n", name, npType)

	// 1. Local directory + git init
	step(1, "Creating local project directory...")
	if err := os.MkdirAll(name, 0755); err != nil {
		return fmt.Errorf("creating folder: %w", err)
	}
	if err := runGit(name, "init", "-b", "main"); err != nil {
		if err2 := runGit(name, "init"); err2 != nil {
			return err2
		}
	}

	// 2. Starter files
	step(2, "Writing starter files...")
	if err := writeStarterFiles(name, npType); err != nil {
		return fmt.Errorf("writing starter files: %w", err)
	}

	// 3. Gitea repo
	step(3, "Creating Gitea repo...")
	client, err := gogitea.NewClient(cfg.URL, gogitea.SetToken(cfg.Token))
	if err != nil {
		return fmt.Errorf("connecting to Gitea: %w", err)
	}
	repo, _, err := client.CreateRepo(gogitea.CreateRepoOption{
		Name:        name,
		Description: repoDesc,
		Private:     npPrivate,
		AutoInit:    false,
	})
	if err != nil {
		return fmt.Errorf("creating Gitea repo: %w", err)
	}
	fmt.Println("   →", repo.HTMLURL)

	// 4. Initial commit and push
	step(4, "Initial commit and push...")
	remote := authCloneURL(cfg, repo.CloneURL)
	if err := runGit(name, "remote", "add", "origin", remote); err != nil {
		return err
	}
	if err := runGit(name, "add", "."); err != nil {
		return err
	}
	if err := runGit(name, "commit", "-m", "Initial commit"); err != nil {
		return err
	}
	if err := runGit(name, "push", "-u", "origin", "main"); err != nil {
		return err
	}

	// Resolve config defaults
	projectsDir := orDefault(cfg.ProjectsDir, "~/projects")
	homelabDir := orDefault(cfg.HomelabDir, "~/homelab")
	domain := orDefault(cfg.Domain, "sketchferret.com")
	caddyDir := orDefault(cfg.CaddyConfDir, "~/caddy/conf.d")
	caddyReload := orDefault(cfg.CaddyReloadCmd, "sudo caddy reload")
	proxyHost := orDefault(cfg.ProxyHost, cfg.SSHHost)
	portStart := cfg.PortStart
	if portStart == 0 {
		portStart = 8200
	}

	// 5. beepc: clone + homelab service
	step(5, fmt.Sprintf("Setting up beepc (%s@%s)...", cfg.SSHUser, cfg.SSHHost))

	// Auto-assign port by counting existing service dirs
	out, _ := sshRun(cfg.SSHUser, cfg.SSHHost,
		fmt.Sprintf("ls %s/services/ 2>/dev/null | wc -l", homelabDir))
	count, _ := strconv.Atoi(strings.TrimSpace(out))
	port := portStart + count

	// Clone to ~/projects/<name>
	remotePath := projectsDir + "/" + name
	cloneURL := authCloneURL(cfg, repo.CloneURL)
	if _, err := sshRun(cfg.SSHUser, cfg.SSHHost,
		fmt.Sprintf("mkdir -p %s && git clone %s %s", projectsDir, cloneURL, remotePath)); err != nil {
		return fmt.Errorf("cloning on beepc: %w", err)
	}
	fmt.Printf("   Cloned → %s\n", remotePath)

	// Homelab service
	serviceDir := homelabDir + "/services/" + name
	if err := sshWriteFile(cfg.SSHUser, cfg.SSHHost,
		serviceDir+"/docker-compose.yml",
		dockerComposeTemplate(name, npType, port)); err != nil {
		return fmt.Errorf("writing docker-compose.yml: %w", err)
	}
	if err := sshWriteFile(cfg.SSHUser, cfg.SSHHost,
		serviceDir+"/.env.example",
		dotEnvExample(name, npType, port)); err != nil {
		return fmt.Errorf("writing .env.example: %w", err)
	}
	fmt.Printf("   Homelab service → %s\n", serviceDir)

	// 6. Caddy
	step(6, "Adding Caddy config...")
	subdomain := name + "." + domain
	if err := sshWriteFile(cfg.SSHUser, cfg.SSHHost,
		caddyDir+"/"+name+".caddy",
		caddySnippet(subdomain, proxyHost, port)); err != nil {
		return fmt.Errorf("writing Caddy snippet: %w", err)
	}
	if _, err := sshRun(cfg.SSHUser, cfg.SSHHost, caddyReload); err != nil {
		fmt.Printf("   Warning: Caddy reload failed — reload manually when ready\n")
	} else {
		fmt.Printf("   https://%s → localhost:%d\n", subdomain, port)
	}

	fmt.Printf(`
Done!
  Local:    ./%s
  Gitea:    %s
  beepc:    %s
  Service:  %s
  URL:      https://%s
  Port:     %d
`, name, repo.HTMLURL, remotePath, serviceDir, subdomain, port)

	return nil
}

func writeStarterFiles(dir, stackType string) error {
	files := map[string]string{
		".gitignore": gitignoreFor(stackType),
		"README.md":  "# " + dir + "\n",
	}
	switch stackType {
	case "node":
		files["package.json"] = nodePackageJSON(dir)
		files["tsconfig.json"] = tsconfigJSON()
		files["src/index.ts"] = nodeIndexTS()
		files["Dockerfile"] = nodeDockerfile()
	case "python":
		files["requirements.txt"] = "# Add dependencies here\n"
		files["app.py"] = pythonApp()
		files["Dockerfile"] = pythonDockerfile()
	case "go":
		files["go.mod"] = goMod(dir)
		files["main.go"] = goMain()
		files["Dockerfile"] = goDockerfile()
	case "static":
		files["index.html"] = staticHTML(dir)
		files["Dockerfile"] = staticDockerfile()
	}
	for relPath, content := range files {
		absPath := filepath.Join(dir, relPath)
		if err := os.MkdirAll(filepath.Dir(absPath), 0755); err != nil {
			return err
		}
		if err := os.WriteFile(absPath, []byte(content), 0644); err != nil {
			return err
		}
	}
	return nil
}

func orDefault(s, def string) string {
	if s != "" {
		return s
	}
	return def
}
