package infra

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

// ScaffoldWailsAppConfig configura la generación del proyecto Wails.
type ScaffoldWailsAppConfig struct {
	Name        string // Nombre del proyecto
	Dir         string // Directorio destino
	Title       string // Título de la ventana
	Width       int    // Ancho de la ventana (default 1024)
	Height      int    // Alto de la ventana (default 768)
	Author      string // Nombre del autor
	FrontendLib string // Path a la frontend library (default ~/.local_agentes/frontend/frontend)
}

const mainGoTpl = `package main

import (
	"embed"

	"github.com/wailsapp/wails/v2"
	"github.com/wailsapp/wails/v2/pkg/options"
	"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)

//go:embed all:frontend/dist
var assets embed.FS

func main() {
	app := NewApp()

	err := wails.Run(&options.App{
		Title:  "{{.Title}}",
		Width:  {{.Width}},
		Height: {{.Height}},
		AssetServer: &assetserver.Options{
			Assets: assets,
		},
		BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
		OnStartup:        app.startup,
		Bind: []interface{}{
			app,
		},
	})

	if err != nil {
		println("Error:", err.Error())
	}
}
`

const appGoTpl = `package main

import (
	"context"

	"github.com/wailsapp/wails/v2/pkg/runtime"
)

// App struct — cada método público se expone como binding IPC al frontend.
type App struct {
	ctx context.Context
}

func NewApp() *App {
	return &App{}
}

func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
}

// EmitEvent emite un evento al frontend.
func (a *App) EmitEvent(eventName string, data interface{}) {
	runtime.EventsEmit(a.ctx, eventName, data)
}

// Ping verifica que el IPC funciona.
func (a *App) Ping() string {
	return "pong"
}
`

const wailsJSONTpl = `{
  "$schema": "https://wails.io/schemas/config.v2.json",
  "name": "{{.Name}}",
  "outputfilename": "{{.Name}}",
  "frontend:dir": "./frontend",
  "frontend:install": "pnpm install",
  "frontend:build": "pnpm run build",
  "frontend:dev:watcher": "pnpm run dev",
  "frontend:dev:serverUrl": "auto",
  "wailsjsdir": "./frontend/src/wailsjs",
  "author": {
    "name": "{{.Author}}"
  }
}
`

const goModTpl = `module {{.Name}}

go 1.23

require github.com/wailsapp/wails/v2 v2.11.0
`

// ScaffoldWailsApp genera la estructura base de un proyecto Wails con frontend vinculado.
func ScaffoldWailsApp(ctx context.Context, cfg ScaffoldWailsAppConfig) error {
	if cfg.Name == "" {
		return fmt.Errorf("name is required")
	}
	if cfg.Dir == "" {
		cfg.Dir = cfg.Name
	}
	if cfg.Title == "" {
		cfg.Title = cfg.Name
	}
	if cfg.Width == 0 {
		cfg.Width = 1024
	}
	if cfg.Height == 0 {
		cfg.Height = 768
	}
	if cfg.Author == "" {
		cfg.Author = "Agent"
	}
	if cfg.FrontendLib == "" {
		home, _ := os.UserHomeDir()
		cfg.FrontendLib = filepath.Join(home, ".local_agentes", "frontend", "frontend")
	}

	// Crear directorio
	if err := os.MkdirAll(cfg.Dir, 0755); err != nil {
		return fmt.Errorf("creating dir: %w", err)
	}

	// Generar archivos Go
	files := map[string]string{
		"main.go":    mainGoTpl,
		"app.go":     appGoTpl,
		"wails.json": wailsJSONTpl,
		"go.mod":     goModTpl,
	}

	for name, tpl := range files {
		path := filepath.Join(cfg.Dir, name)
		t, err := template.New(name).Parse(tpl)
		if err != nil {
			return fmt.Errorf("parsing template %s: %w", name, err)
		}
		f, err := os.Create(path)
		if err != nil {
			return fmt.Errorf("creating %s: %w", name, err)
		}
		if err := t.Execute(f, cfg); err != nil {
			f.Close()
			return fmt.Errorf("executing template %s: %w", name, err)
		}
		f.Close()
	}

	// Crear frontend como link simbólico o copiar template
	frontendDir := filepath.Join(cfg.Dir, "frontend")
	createProjectScript := filepath.Join(filepath.Dir(cfg.FrontendLib), "..", "scripts", "create-project.sh")

	if _, err := os.Stat(createProjectScript); err == nil {
		// Usar el script de Frontend_Library
		cmd := exec.CommandContext(ctx, "bash", createProjectScript, cfg.Name, frontendDir, "--wails")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("creating frontend: %w", err)
		}
	} else {
		// Fallback: crear frontend mínimo
		if err := os.MkdirAll(filepath.Join(frontendDir, "src"), 0755); err != nil {
			return fmt.Errorf("creating frontend dir: %w", err)
		}
		pkgJSON := fmt.Sprintf(`{"name":"%s-frontend","private":true,"scripts":{"dev":"vite","build":"vite build"}}`, cfg.Name)
		os.WriteFile(filepath.Join(frontendDir, "package.json"), []byte(pkgJSON), 0644)
	}

	// go mod tidy
	cmd := exec.CommandContext(ctx, "go", "mod", "tidy")
	cmd.Dir = cfg.Dir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	_ = cmd.Run() // No fatal si falla

	fmt.Printf("Wails app scaffolded at %s\n", cfg.Dir)
	return nil
}

// GenerateAppBinding genera el código Go para un método de binding.
func GenerateAppBinding(name string, params []string, returnType string, body string) string {
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("func (a *App) %s(", name))
	sb.WriteString(strings.Join(params, ", "))
	sb.WriteString(")")
	if returnType != "" {
		sb.WriteString(" " + returnType)
	}
	sb.WriteString(" {\n")
	sb.WriteString("\t" + body + "\n")
	sb.WriteString("}\n")
	return sb.String()
}
