#!/usr/bin/env bash
# init_web_app
# ------------
# Scaffold de full-stack app: Go HTTP API backend + React frontend con
# Mantine y @fn_library. Extiende init_api_app anadiendo la capa frontend.
#
# Genera todo lo de init_api_app mas frontend/ con pnpm + vite + react +
# mantine, vite.config.ts con alias @fn_library y proxy al backend,
# src/main.tsx con FnMantineProvider, src/App.tsx, src/theme.ts y
# src/pages/Home.tsx de ejemplo.
#
# USO:
#   ./init_web_app.sh <nombre> [--port N] [--with-auth] [--with-db]
#
# EJEMPLO:
#   ./init_web_app.sh my_dashboard
#   ./init_web_app.sh my_dashboard --port 8080 --with-auth

set -euo pipefail

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

# Source funciones atomicas
source "$REGISTRY_ROOT/bash/functions/shell/assert_command_exists.sh"

# ── Parsing ──────────────────────────────────────────────────

NOMBRE=""
PORT="8080"
WITH_AUTH="false"
WITH_DB="false"
SKIP_PNPM_BUILD="${SKIP_PNPM_BUILD:-false}"

while [ $# -gt 0 ]; do
    case "$1" in
        --port) PORT="$2"; shift 2 ;;
        --with-auth) WITH_AUTH="true"; shift ;;
        --with-db) WITH_DB="true"; shift ;;
        --skip-pnpm-build) SKIP_PNPM_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> [--port N] [--with-auth] [--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 WEB APP: ${NOMBRE}"
echo "  Directorio: ${APP_DIR}"
echo "  Puerto:     ${PORT}"
echo "  Auth:       ${WITH_AUTH}"
echo "  DB:         ${WITH_DB}"
echo "════════════════════════════════════════════════════════════"
echo ""

# ── 1. Invocar init_api_app para generar backend ─────────────

echo "[1/3] Generando backend con init_api_app..."
BACKEND_FLAGS=()
BACKEND_FLAGS+=(--port "$PORT")
[ "$WITH_AUTH" = "true" ] && BACKEND_FLAGS+=(--with-auth)
[ "$WITH_DB" = "true" ] && BACKEND_FLAGS+=(--with-db)

bash "$SCRIPT_DIR/init_api_app.sh" "$NOMBRE" "${BACKEND_FLAGS[@]}"

# ── 2. Generar frontend ──────────────────────────────────────

echo ""
echo "[2/3] Generando frontend..."
mkdir -p "$APP_DIR/frontend/src/pages"

# package.json
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",
    "@mantine/notifications": "^8.0.0",
    "@tabler/icons-react": "^3.30.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-router-dom": "^7.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

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

// Vite config: proxy API al backend Go + alias @fn_library a frontend/functions/ui del registry
export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      "@fn_library": path.resolve(__dirname, "../../../frontend/functions/ui"),
    },
  },
  server: {
    port: 5173,
    proxy: {
      "/api": {
        target: "http://localhost:${PORT}",
        changeOrigin: true,
      },
      "/health": {
        target: "http://localhost:${PORT}",
      },
    },
  },
  build: {
    outDir: "dist",
  },
});
EOF

# tsconfig.json
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,
    "allowImportingTsExtensions": false,
    "baseUrl": ".",
    "paths": {
      "@fn_library/*": ["../../../frontend/functions/ui/*"]
    }
  },
  "include": ["src"]
}
EOF

# index.html
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

# postcss.config.cjs (Mantine usa postcss-preset-mantine)
cat > "$APP_DIR/frontend/postcss.config.cjs" <<'EOF'
module.exports = {
  plugins: {
    "postcss-preset-mantine": {},
    "postcss-simple-vars": {
      variables: {
        "mantine-breakpoint-xs": "36em",
        "mantine-breakpoint-sm": "48em",
        "mantine-breakpoint-md": "62em",
        "mantine-breakpoint-lg": "75em",
        "mantine-breakpoint-xl": "88em",
      },
    },
  },
};
EOF

# src/theme.ts
cat > "$APP_DIR/frontend/src/theme.ts" <<EOF
import { createTheme } from "@mantine/core";

// Tema Mantine de ${NOMBRE}. Mantine genera sus propias CSS variables.
export const theme = createTheme({
  primaryColor: "blue",
  defaultRadius: "md",
});
EOF

# src/main.tsx
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="auto">
      <App />
    </MantineProvider>
  </React.StrictMode>,
);
EOF

# src/App.tsx
cat > "$APP_DIR/frontend/src/App.tsx" <<EOF
import { AppShell, Title, Burger, Group, NavLink, Stack } from "@mantine/core";
import { useDisclosure } from "@mantine/hooks";
import { IconHome, IconSettings } from "@tabler/icons-react";

import { Home } from "./pages/Home";

export function App() {
  const [opened, { toggle }] = useDisclosure();

  return (
    <AppShell
      header={{ height: 56 }}
      navbar={{ width: 240, breakpoint: "sm", collapsed: { mobile: !opened } }}
      padding="md"
    >
      <AppShell.Header>
        <Group h="100%" px="md">
          <Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
          <Title order={4}>${NOMBRE}</Title>
        </Group>
      </AppShell.Header>

      <AppShell.Navbar p="sm">
        <Stack gap={4}>
          <NavLink label="Home" leftSection={<IconHome size={16} />} active />
          <NavLink label="Settings" leftSection={<IconSettings size={16} />} />
        </Stack>
      </AppShell.Navbar>

      <AppShell.Main>
        <Home />
      </AppShell.Main>
    </AppShell>
  );
}
EOF

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

type StatusResponse = {
  app: string;
  version: string;
};

export function Home() {
  const [status, setStatus] = useState<StatusResponse | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch("/api/v1/status")
      .then((r) => r.json())
      .then(setStatus)
      .catch((e) => setError(String(e)));
  }, []);

  return (
    <Stack>
      <Title order={2}>${NOMBRE}</Title>
      <Text c="dimmed">Full-stack app scaffoldeada por init_web_app.</Text>

      <Paper p="md" withBorder>
        <Group justify="space-between">
          <Text fw={500}>API status</Text>
          {status ? (
            <Badge color="green">{status.app} v{status.version}</Badge>
          ) : error ? (
            <Badge color="red">{error}</Badge>
          ) : (
            <Badge color="gray">loading...</Badge>
          )}
        </Group>
      </Paper>
    </Stack>
  );
}
EOF

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

# docker-compose.yml
cat > "$APP_DIR/docker-compose.yml" <<EOF
services:
  api:
    build: .
    ports:
      - "${PORT}:${PORT}"
    env_file: .env
    volumes:
      - ./migrations:/app/migrations
  frontend:
    image: node:20-alpine
    working_dir: /app
    command: sh -c "corepack enable && pnpm install && pnpm dev --host"
    ports:
      - "5173:5173"
    volumes:
      - ./frontend:/app
EOF

# Actualizar Makefile con targets frontend + dev
cat > "$APP_DIR/Makefile" <<EOF
.PHONY: build build-frontend run dev test vet clean install

BIN=${NOMBRE}

install:
	cd frontend && pnpm install

build-frontend:
	cd frontend && pnpm build

build: build-frontend
	CGO_ENABLED=1 go build -tags fts5 -o \$(BIN) .

run: build
	./\$(BIN)

dev:
	@echo "Arranca el backend (API en :${PORT}):"
	@echo "  CGO_ENABLED=1 go run ."
	@echo "Arranca el frontend (Vite en :5173 con proxy):"
	@echo "  cd frontend && pnpm dev"

test:
	CGO_ENABLED=1 go test -tags fts5 -v ./...

vet:
	CGO_ENABLED=1 go vet -tags fts5 ./...

clean:
	rm -f \$(BIN) *.db *.db-shm *.db-wal
	rm -rf frontend/dist frontend/node_modules
EOF

# Actualizar app.md con framework + uses frontend
cat > "$APP_DIR/app.md" <<EOF
---
name: ${NOMBRE}
lang: go
domain: tools
description: "Full-stack app (Go API + React/Mantine frontend) generada por init_web_app."
tags: [service, web, frontend]
uses_functions:
  - http_serve_go_infra
  - http_router_go_infra
  - http_middleware_chain_go_infra
  - http_cors_middleware_go_infra
  - http_logger_middleware_go_infra
  - http_json_response_go_infra
  - http_error_response_go_infra
  - migration_up_go_infra
uses_types:
  - Route_go_infra
  - Middleware_go_infra
  - HTTPError_go_infra
framework: "net/http + vite + react + mantine"
entry_point: "main.go"
dir_path: "apps/${NOMBRE}"
---

## Notas

App full-stack: backend en Go (\`main.go\`) + frontend en \`frontend/\` con
Vite + React + Mantine. El vite.config.ts hace proxy de \`/api\` y \`/health\`
al backend en :${PORT}, y usa alias \`@fn_library\` hacia \`frontend/functions/ui\`
del registry.

Dev: \`make install && make dev\` (dos terminales: backend con \`go run .\`,
frontend con \`cd frontend && pnpm dev\`).

Prod: \`make build\` genera \`frontend/dist\` + binario; el binario sirve los
static files del build embebido (pendiente embed en main.go).
EOF

# ── 3. Verificar frontend si pnpm disponible ─────────────────

echo ""
echo "[3/3] Verificacion..."

if [ "$SKIP_PNPM_BUILD" = "true" ]; then
    echo "  SKIP_PNPM_BUILD=true — saltando pnpm install/build"
elif command -v pnpm >/dev/null 2>&1; then
    echo "  pnpm detectado, ejecutando install + build..."
    (
        cd "$APP_DIR/frontend"
        if pnpm install --silent 2>&1 | tail -5; then
            :
        fi
        if pnpm build 2>&1 | tail -5; then
            echo "  frontend build OK"
        else
            echo "  WARN: frontend build fallo (revisa el output arriba)" >&2
        fi
    )
else
    echo "  pnpm no disponible — saltando verificacion frontend"
    echo "  Cuando este disponible: cd ${APP_DIR}/frontend && pnpm install && pnpm build"
fi

echo ""
echo "════════════════════════════════════════════════════════════"
echo "  WEB APP '${NOMBRE}' LISTA"
echo "════════════════════════════════════════════════════════════"
echo ""
echo "  Pasos siguientes:"
echo "    cd apps/${NOMBRE}"
echo "    cp .env.example .env"
echo "    make install         # pnpm install del frontend"
echo ""
echo "  Desarrollo (2 terminales):"
echo "    Terminal 1: cd apps/${NOMBRE} && CGO_ENABLED=1 go run ."
echo "    Terminal 2: cd apps/${NOMBRE}/frontend && pnpm dev"
echo ""
echo "  Abrir http://localhost:5173"
echo ""
