#!/usr/bin/env bash
# init_desktop_app
# ----------------
# Scaffold de Wails desktop app: Go backend + React frontend con @mantine y
# @fn_library. Genera main.go (Wails con embed del frontend), app.go (struct
# App con bindings base), wails.json, go.mod y frontend/ con vite+react+mantine.
#
# USO:
#   ./init_desktop_app.sh <nombre> [--with-db]
#
# FLAGS:
#   --with-db   Anade store.go con SQLite + bindings CRUD de ejemplo
#
# EJEMPLO:
#   ./init_desktop_app.sh my_tool
#   ./init_desktop_app.sh data_explorer --with-db

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REGISTRY_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"

source "$REGISTRY_ROOT/bash/functions/shell/assert_command_exists.sh"

NOMBRE=""
WITH_DB="false"
SKIP_WAILS_BUILD="${SKIP_WAILS_BUILD:-false}"

while [ $# -gt 0 ]; do
    case "$1" in
        --with-db) WITH_DB="true"; shift ;;
        --skip-wails-build) SKIP_WAILS_BUILD="true"; shift ;;
        -h|--help) grep "^#" "$0" | sed 's/^# \?//' ; exit 0 ;;
        -*) echo "Flag desconocido: $1" >&2 ; exit 1 ;;
        *)
            if [ -z "$NOMBRE" ]; then NOMBRE="$1"
            else echo "Argumento extra ignorado: $1" >&2
            fi
            shift ;;
    esac
done

if [ -z "$NOMBRE" ]; then
    echo "Uso: $0 <nombre> [--with-db]" >&2
    exit 1
fi

APP_DIR="${REGISTRY_ROOT}/apps/${NOMBRE}"
if [ -d "$APP_DIR" ]; then
    echo "ERROR: ${APP_DIR} ya existe. Abortando." >&2
    exit 1
fi

echo ""
echo "════════════════════════════════════════════════════════════"
echo "  INIT DESKTOP APP (Wails): ${NOMBRE}"
echo "  Directorio: ${APP_DIR}"
echo "  DB:         ${WITH_DB}"
echo "════════════════════════════════════════════════════════════"
echo ""

# ── 1. Verificar Go y Wails ──────────────────────────────────

echo "[1/5] Verificando herramientas..."
assert_command_exists go
echo "  Go: $(go version)"

if command -v wails >/dev/null 2>&1; then
    echo "  Wails: $(wails version 2>/dev/null || echo detectado)"
else
    echo "  WARN: Wails CLI no detectado."
    echo "        Instalar: source ${REGISTRY_ROOT}/bash/functions/infra/install_wails.sh && install_wails"
    echo "        (Continuando con scaffold; \`wails build\` requiere el CLI.)"
fi

# ── 2. Crear estructura + archivos Go ────────────────────────

echo "[2/5] Creando estructura y archivos Go..."
mkdir -p "$APP_DIR/frontend/src/pages"
mkdir -p "$APP_DIR/build/bin"

# go.mod con replace a fn-registry
cat > "$APP_DIR/go.mod" <<EOF
module ${NOMBRE}

go 1.25.0

require (
	github.com/wailsapp/wails/v2 v2.9.2
)

require (
	fn-registry v0.0.0-00010101000000-000000000000
)

replace fn-registry => ${REGISTRY_ROOT}
EOF

# main.go: entry point Wails con embed frontend/dist
cat > "$APP_DIR/main.go" <<'EOF'
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:  "__APP_NAME__",
		Width:  1024,
		Height: 768,
		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())
	}
}
EOF
sed -i "s/__APP_NAME__/${NOMBRE}/g" "$APP_DIR/main.go"

# app.go: struct App con bindings base
cat > "$APP_DIR/app.go" <<EOF
package main

import (
	"context"
	"fmt"
	"runtime"
)

// App struct — contexto de la app Wails y bindings al frontend.
type App struct {
	ctx context.Context
}

// NewApp crea una nueva App.
func NewApp() *App {
	return &App{}
}

// startup se llama cuando la app arranca. Persistimos el ctx para usarlo en
// llamadas a runtime.EventsEmit, etc.
func (a *App) startup(ctx context.Context) {
	a.ctx = ctx
}

// Greet retorna un saludo. Accesible desde el frontend como wails.Greet(name).
func (a *App) Greet(name string) string {
	return fmt.Sprintf("Hola, %s!", name)
}

// GetVersion retorna la version de la app y el Go runtime.
func (a *App) GetVersion() map[string]string {
	return map[string]string{
		"app":     "0.1.0",
		"name":    "${NOMBRE}",
		"goVer":   runtime.Version(),
		"goOS":    runtime.GOOS,
	}
}
EOF

# wails.json
cat > "$APP_DIR/wails.json" <<EOF
{
  "\$schema": "https://wails.io/schemas/config.v2.json",
  "name": "${NOMBRE}",
  "outputfilename": "${NOMBRE}",
  "frontend:install": "pnpm install",
  "frontend:build": "pnpm build",
  "frontend:dev:watcher": "pnpm dev",
  "frontend:dev:serverUrl": "auto",
  "author": {
    "name": "",
    "email": ""
  }
}
EOF

# Store opcional
if [ "$WITH_DB" = "true" ]; then
    cat > "$APP_DIR/store.go" <<'EOF'
package main

import (
	"database/sql"
	"fmt"

	_ "github.com/mattn/go-sqlite3"
)

// Item es un registro de ejemplo.
type Item struct {
	ID        int64  `json:"id"`
	Name      string `json:"name"`
	CreatedAt string `json:"createdAt"`
}

// openDB abre (o crea) la base de datos SQLite en dbPath e inicializa el schema.
func openDB(dbPath string) (*sql.DB, error) {
	db, err := sql.Open("sqlite3", dbPath)
	if err != nil {
		return nil, fmt.Errorf("open db: %w", err)
	}
	if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS items (
	id INTEGER PRIMARY KEY AUTOINCREMENT,
	name TEXT NOT NULL,
	created_at TEXT NOT NULL DEFAULT (datetime('now'))
)`); err != nil {
		return nil, fmt.Errorf("init schema: %w", err)
	}
	return db, nil
}

// ListItems devuelve todos los items. Binding accesible desde el frontend.
func (a *App) ListItems() ([]Item, error) {
	rows, err := a.db.Query(`SELECT id, name, created_at FROM items ORDER BY id DESC`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var items []Item
	for rows.Next() {
		var it Item
		if err := rows.Scan(&it.ID, &it.Name, &it.CreatedAt); err != nil {
			return nil, err
		}
		items = append(items, it)
	}
	return items, nil
}

// CreateItem inserta un nuevo item.
func (a *App) CreateItem(name string) (Item, error) {
	res, err := a.db.Exec(`INSERT INTO items (name) VALUES (?)`, name)
	if err != nil {
		return Item{}, err
	}
	id, _ := res.LastInsertId()
	return Item{ID: id, Name: name}, nil
}
EOF
    # Cuando hay DB, actualizamos app.go para incluir db field
    cat > "$APP_DIR/app.go" <<EOF
package main

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"runtime"
)

// App struct — contexto de la app Wails, DB y bindings al frontend.
type App struct {
	ctx context.Context
	db  *sql.DB
}

// NewApp crea una nueva App con la DB abierta.
func NewApp() *App {
	db, err := openDB("${NOMBRE}.db")
	if err != nil {
		log.Fatalf("open db: %v", err)
	}
	return &App{db: db}
}

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

func (a *App) Greet(name string) string {
	return fmt.Sprintf("Hola, %s!", name)
}

func (a *App) GetVersion() map[string]string {
	return map[string]string{
		"app":   "0.1.0",
		"name":  "${NOMBRE}",
		"goVer": runtime.Version(),
		"goOS":  runtime.GOOS,
	}
}
EOF
    # Actualizar go.mod para incluir mattn/go-sqlite3
    cat > "$APP_DIR/go.mod" <<EOF
module ${NOMBRE}

go 1.25.0

require (
	github.com/wailsapp/wails/v2 v2.9.2
	github.com/mattn/go-sqlite3 v1.14.37
)

require (
	fn-registry v0.0.0-00010101000000-000000000000
)

replace fn-registry => ${REGISTRY_ROOT}
EOF
fi

# ── 3. Frontend ──────────────────────────────────────────────

echo "[3/5] Generando frontend..."

cat > "$APP_DIR/frontend/package.json" <<EOF
{
  "name": "${NOMBRE}-frontend",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@mantine/core": "^8.0.0",
    "@mantine/hooks": "^8.0.0",
    "@tabler/icons-react": "^3.30.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vitejs/plugin-react": "^5.0.0",
    "typescript": "~5.7.0",
    "vite": "^7.0.0"
  }
}
EOF

cat > "$APP_DIR/frontend/vite.config.ts" <<'EOF'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

// Vite config para Wails: build → dist/, alias @fn_library a frontend/functions/ui
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@fn_library": path.resolve(__dirname, "../../../frontend/functions/ui"),
    },
  },
  build: {
    outDir: "dist",
    emptyOutDir: true,
  },
});
EOF

cat > "$APP_DIR/frontend/tsconfig.json" <<'EOF'
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "@fn_library/*": ["../../../frontend/functions/ui/*"]
    }
  },
  "include": ["src"]
}
EOF

cat > "$APP_DIR/frontend/index.html" <<EOF
<!doctype html>
<html lang="es">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>${NOMBRE}</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
EOF

cat > "$APP_DIR/frontend/postcss.config.cjs" <<'EOF'
module.exports = {
  plugins: {
    "postcss-preset-mantine": {},
  },
};
EOF

cat > "$APP_DIR/frontend/src/theme.ts" <<'EOF'
import { createTheme } from "@mantine/core";
export const theme = createTheme({
  primaryColor: "blue",
  defaultRadius: "md",
});
EOF

cat > "$APP_DIR/frontend/src/main.tsx" <<'EOF'
import React from "react";
import ReactDOM from "react-dom/client";
import { MantineProvider } from "@mantine/core";
import "@mantine/core/styles.css";

import { theme } from "./theme";
import { App } from "./App";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <MantineProvider theme={theme} defaultColorScheme="dark">
      <App />
    </MantineProvider>
  </React.StrictMode>,
);
EOF

cat > "$APP_DIR/frontend/src/App.tsx" <<EOF
import { useEffect, useState } from "react";
import { Stack, Title, Text, Paper, Button, Group, Badge } from "@mantine/core";

// @ts-expect-error Wails genera este modulo al hacer build
import { Greet, GetVersion } from "../wailsjs/go/main/App";

export function App() {
  const [greeting, setGreeting] = useState("");
  const [version, setVersion] = useState<Record<string, string> | null>(null);

  useEffect(() => {
    GetVersion().then(setVersion).catch(() => setVersion(null));
  }, []);

  return (
    <Stack p="xl">
      <Title order={2}>${NOMBRE}</Title>
      <Text c="dimmed">Desktop app scaffoldeada por init_desktop_app.</Text>

      <Paper p="md" withBorder>
        <Group justify="space-between">
          <Text fw={500}>Version</Text>
          <Badge color="blue">
            {version ? \`\${version.app} (\${version.goOS} \${version.goVer})\` : "cargando..."}
          </Badge>
        </Group>
      </Paper>

      <Paper p="md" withBorder>
        <Stack>
          <Button onClick={() => Greet("desktop").then(setGreeting)}>
            Greet from Go
          </Button>
          {greeting && <Text>{greeting}</Text>}
        </Stack>
      </Paper>
    </Stack>
  );
}
EOF

cat > "$APP_DIR/frontend/.gitignore" <<'EOF'
node_modules/
dist/
wailsjs/
*.log
EOF

# .gitignore raiz
cat > "$APP_DIR/.gitignore" <<EOF
# Wails build output
build/bin/
frontend/dist/
frontend/wailsjs/
frontend/node_modules/

# SQLite
*.db
*.db-shm
*.db-wal

# IDE
.idea/
.vscode/
EOF

# ── 4. app.md ────────────────────────────────────────────────

echo "[4/5] Escribiendo app.md..."

USES_FUNCTIONS='  - scaffold_wails_app_go_infra
  - install_wails_bash_infra'

cat > "$APP_DIR/app.md" <<EOF
---
name: ${NOMBRE}
lang: go
domain: tools
description: "Desktop app (Wails + React + Mantine) generada por init_desktop_app."
tags: [desktop, wails, frontend]
uses_functions:
${USES_FUNCTIONS}
uses_types: []
framework: "wails + vite + react + mantine"
entry_point: "main.go"
dir_path: "apps/${NOMBRE}"
---

## Notas

Desktop app con backend Go (Wails v2) y frontend React. Bindings en
\`app.go\` son accesibles desde el frontend via \`wails dev\`/\`wails build\`
que genera \`frontend/wailsjs/go/main/App.ts\` automaticamente.

Desarrollo:

\`\`\`bash
cd apps/${NOMBRE}
cd frontend && pnpm install && cd ..
wails dev
\`\`\`

Build:

\`\`\`bash
wails build
./build/bin/${NOMBRE}
\`\`\`
EOF

# ── 5. Verificacion ──────────────────────────────────────────

echo "[5/5] Verificacion..."

if [ "$SKIP_WAILS_BUILD" = "true" ]; then
    echo "  SKIP_WAILS_BUILD=true — saltando wails build"
elif command -v wails >/dev/null 2>&1; then
    echo "  wails detectado. Ejecutar manualmente 'wails build' en ${APP_DIR}"
    echo "  (el build real requiere pnpm install previo en frontend/)"
else
    echo "  wails no disponible — saltando build."
    echo "  El scaffold esta listo. Para builds: instalar Wails primero."
fi

# Al menos verificar que go mod tidy funciona (que el main.go/app.go es Go valido)
(
    cd "$APP_DIR"
    if CGO_ENABLED=1 go mod tidy 2>&1 | tail -5; then
        echo "  go mod tidy OK"
    else
        echo "  WARN: go mod tidy fallo — revisa main.go/app.go/go.mod" >&2
    fi
)

echo ""
echo "════════════════════════════════════════════════════════════"
echo "  DESKTOP APP '${NOMBRE}' LISTA"
echo "════════════════════════════════════════════════════════════"
echo ""
echo "  Pasos siguientes:"
echo "    cd apps/${NOMBRE}/frontend && pnpm install && cd .."
echo "    wails dev     # modo desarrollo con hot reload"
echo "    wails build   # binario de produccion en build/bin/"
echo ""
