modules/frontend/Write-Frontend-NextJs-Config.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$OutputPath,
    [string]$FrontendMode,
    [string[]]$Entities,
    [string]$Language = 'tr'
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")
. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Get-Locale.ps1")
$L = Get-Locale -Language $Language

$BT    = "``"
$base = Join-Path $OutputPath "frontend"
function Write-File($rel, $rawContent) {
    $content = $rawContent -replace 'BTICK', $BT
    Write-CoreFile (Join-Path $base $rel) $content
}
function Proj($t) { $t -replace '__PROJ__', $ProjectName }

# create-next-app demo dosyalarini temizle
@("app\page.tsx","app\layout.tsx","app\globals.css") | ForEach-Object {
    Remove-Item (Join-Path $base $_) -ErrorAction SilentlyContinue
}
Remove-Item (Join-Path $base "app\fonts")                            -Recurse -ErrorAction SilentlyContinue
Remove-Item (Join-Path $base "public")                               -Recurse -ErrorAction SilentlyContinue
if (-not $Global:DryRun) {
    New-Item -ItemType Directory -Path (Join-Path $base "public") -Force | Out-Null
    # Docker build icin public dizini var olmali; Next.js standalone output bunu gerektirir
    [System.IO.File]::WriteAllText((Join-Path $base "public\.gitkeep"), "")
}

# ─── TYPES ────────────────────────────────────────────────
$typeInterfaces = ""
foreach ($e in $Entities) {
    if ($e -eq "User") { continue }
    $extraType = if ($e -eq "Product") { " price: number;" } else { "" }
    $typeInterfaces += "export interface $e { id: number; name: string;$extraType createdAt: string; }`n"
}

Write-File "types\index.ts" (Proj @"
export interface LoginRequest { username: string; password: string; }
export interface LoginResponse { token: string; username: string; }
$typeInterfaces
export interface ApiError { message?: string; errors?: string[]; }
"@
)

# ─── SERVICES ─────────────────────────────────────────────
Write-File "services\authService.ts" (Proj @"
import type { LoginRequest, LoginResponse } from '@/types';
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8080/api';
export const authService = {
  login: async (data: LoginRequest): Promise<LoginResponse> => {
    const res = await fetch(BTICK`${API_BASE}/auth/loginBTICK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    const json = await res.json();
    if (!res.ok) throw new Error(json.errors?.[0] ?? json.message ?? '$($L.uiLoginFailed)');
    return json as LoginResponse;
  },
  logout: () => { if (typeof window === 'undefined') return; localStorage.removeItem('token'); localStorage.removeItem('username'); },
  getToken: () => typeof window !== 'undefined' ? localStorage.getItem('token') : null,
  getUsername: () => typeof window !== 'undefined' ? localStorage.getItem('username') : null,
  saveSession: (d: LoginResponse) => { localStorage.setItem('token', d.token); localStorage.setItem('username', d.username); },
};
"@
)

foreach ($e in $Entities) {
    if ($e -eq "User") { continue }
    $pE = Get-Plural $e
    $lowE = $e.ToLower()
    $addParams = if ($e -eq "Product") { "name: string, price: number" } else { "name: string" }
    $addBody = if ($e -eq "Product") { "{ name, price }" } else { "{ name }" }

    Write-File "services\$lowE`Service.ts" (Proj @"
import type { $e } from '@/types';
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:8080/api';
const authHeaders = () => ({
  'Content-Type': 'application/json',
  Authorization: BTICK Bearer `${localStorage.getItem('token')}BTICK,
});
export const $lowE`Service = {
  getAll: async (): Promise<$e`[]> => {
    const res = await fetch(BTICK`${API_BASE}/$pE`BTICK, { headers: authHeaders() });
    if (res.status === 401) throw new Error('UNAUTHORIZED');
    if (!res.ok) throw new Error('$pE yuklenemedi.');
    return res.json();
  },
  add: async ($addParams): Promise<{ id: number }> => {
    const res = await fetch(BTICK`${API_BASE}/$pE`BTICK, {
      method: 'POST', headers: authHeaders(), body: JSON.stringify($addBody),
    });
    if (res.status === 401) throw new Error('UNAUTHORIZED');
    const json = await res.json();
    if (!res.ok) throw new Error(json.errors?.[0] ?? json.message ?? '$e eklenemedi.');
    return json;
  },
};
"@
)
}

# ─── CONTEXT ──────────────────────────────────────────────
Write-File "context\AuthContext.tsx" @'
'use client';
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react';
import { authService } from '@/services/authService';
interface AuthContextType {
  username: string | null;
  isAuthenticated: boolean;
  login: (token: string, username: string) => void;
  logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
  const [username, setUsername] = useState<string | null>(null);
  useEffect(() => {
    setUsername(authService.getUsername());
  }, []);
  const login = (token: string, uname: string) => { authService.saveSession({ token, username: uname }); setUsername(uname); };
  const logout = () => { authService.logout(); setUsername(null); };
  return (
    <AuthContext.Provider value={{ username, isAuthenticated: !!username, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}
export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error('useAuth must be used within AuthProvider');
  return ctx;
}
'@


# ─── LAYOUT & GLOBALS ─────────────────────────────────────
Write-File "app\layout.tsx" (Proj @"
import { AuthProvider } from '@/context/AuthContext';
import './globals.css';
export const metadata = { title: '__PROJ__', description: 'Clean Architecture Generator Output' };
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang='tr'>
      <body>
        <AuthProvider>{children}</AuthProvider>
      </body>
    </html>
  );
}
"@
)

Write-File "app\globals.css" @'
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; color: #1a1a2e; -webkit-font-smoothing: antialiased; }
a { color: inherit; text-decoration: none; }
button, input { font-family: inherit; }
'@


Write-File ".env.local"  "NEXT_PUBLIC_API_URL=http://localhost:8080/api`n"
Write-File ".env.docker" "NEXT_PUBLIC_API_URL=http://api:8080/api`n"

Write-File "Dockerfile" @'
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
 
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
 
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
'@


Write-File ".dockerignore" @'
node_modules
.next
.git
.env.local
*.md
'@


Write-File "next.config.mjs" @'
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
};
export default nextConfig;
'@


Write-File "tsconfig.json" @'
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [{ "name": "next" }],
    "paths": {
      "@/*": ["./*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
'@