cleanarch-new.ps1

#Requires -Version 5.1
[CmdletBinding()]
param(
    [string]$ProjectName    = "",
    [ValidateSet("Dapper","EFCore","Hybrid","")]
    [string]$OrmMode        = "",
    [ValidateSet("ReactTS","ReactJS","NextJS","")]
    [string]$FrontendMode   = "",
    [string[]]$Entities     = @("User", "Product"),
    [ValidateSet("MSSQL","Postgres","SQLite")]
    [string]$DbProvider     = "",
    [string]$OutputRootPath = "",
    [string]$ConnectionString = "",
    [switch]$SkipFrontend,
    [switch]$SkipAiGuide,
    [switch]$SkipTests,
    [switch]$SkipCi,
    [switch]$DryRun,
    [string]$IdeConfig      = "VSCode",
    [switch]$Observability,
    [switch]$NonInteractive,
    [switch]$I18n,
    [ValidateSet("GitHub","GitLab","None")]
    [string]$CiProvider     = "GitHub",
    [ValidateSet("tr","en")]
    [string]$Language        = "tr",
    [ValidateSet("Web","Mobile","Both","")]
    [string]$Platform        = "",
    [ValidateSet("Layered","FeatureSliced")]
    [string]$Architecture    = "Layered",
    [ValidateSet("Expo")]
    [string]$MobileTooling   = "Expo",
    [switch]$SkipNpm,
    [switch]$Version,
    [Alias("h")]
    [switch]$Help
)

$ScriptDir  = Split-Path -Parent $MyInvocation.MyCommand.Path
$ModulesDir = Join-Path $ScriptDir "modules"

# --- UTF-8 encoding (Türkçe karakter desteği) ---
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding            = [System.Text.Encoding]::UTF8

# --- Performance Optimizations (DotNet CLI) ---
$env:DOTNET_CLI_TELEMETRY_OPTOUT = "1"
$env:DOTNET_NOLOGO = "1"
$env:NUGET_XMLDOC_MODE = "skip"

$Global:DryRun = $DryRun

. "$ModulesDir\core\Utils.ps1"
. "$ModulesDir\core\Invoke-TuiMenu.ps1"
. "$ModulesDir\core\Get-Locale.ps1"
$L = Get-Locale -Language $Language

if ($Help) {
    Write-Host ""
    Write-Host " Clean Architecture Template Generator (cleanarch)" -ForegroundColor Cyan
    Write-Host " ------------------------------------------------" -ForegroundColor DarkCyan
    Write-Host " Kullanim: cleanarch-new [Parametreler]" -ForegroundColor White
    Write-Host ""
    Write-Host " PARAMETRELER:" -ForegroundColor Yellow
    Write-Host " -ProjectName <Ad> : Projenin adi (Bos birakilirsa sorulur)"
    Write-Host " -OrmMode <Mod> : Dapper, EFCore, Hybrid"
    Write-Host " -DbProvider <Db> : MSSQL, Postgres, SQLite"
    Write-Host " -FrontendMode <Fe> : ReactTS, ReactJS, NextJS"
    Write-Host " -Entities <Liste> : Virgulle ayrilmis entity listesi (User,Product,...)"
    Write-Host " -OutputRootPath <Path> : Projenin uretilecegi dizin"
    Write-Host " -CiProvider <Provider> : GitHub, GitLab, None (Varsayilan: GitHub)"
    Write-Host " -Language <Dil> : tr, en (Varsayilan: tr)"
    Write-Host ""
    Write-Host " OPSIYONEL FLAGLER:" -ForegroundColor Yellow
    Write-Host " -SkipFrontend : Frontend katmanini atlar"
    Write-Host " -SkipAiGuide : AI rehberini olusturmaz"
    Write-Host " -SkipTests : Test projelerini atlar"
    Write-Host " -SkipCi : CI/CD pipeline dosyalarini atlar"
    Write-Host " -Observability : Serilog ve OpenTelemetry altyapisini kurar"
    Write-Host " -I18n : i18next + react-i18next ile cok dil destegi ekler (en/tr)"
    Write-Host " -NonInteractive : TUI menusunu devre disi birakir, varsayilanlari kullanir"
    Write-Host " -DryRun : Dosya yazmadan islemi simule eder"
    Write-Host ""
    Write-Host " DIGER KOMUTLAR:" -ForegroundColor Yellow
    Write-Host " cleanarch-version : Versiyon ve degisiklik bilgilerini gosterir"
    Write-Host " cleanarch-update : Generator'i son surume gunceller"
    Write-Host " cleanarch-uninstall : Generator'i sistemden kaldirir"
    Write-Host ""
    Write-Host " Detayli dokumantasyon: docs/README.md" -ForegroundColor DarkGray
    Write-Host ""
    return
}


if ($Version) {

    $v = & "$ModulesDir\core\cleanarch-version.ps1" -OnlyVersion
    Write-Host "Clean Architecture Template Generator v$v"
    return
}


$TotalSw = [System.Diagnostics.Stopwatch]::StartNew()

if (-not $NonInteractive) { Clear-Host }
Write-Host "================================================" -ForegroundColor DarkCyan
Write-Host " Clean Architecture Template Generator " -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor DarkCyan
Write-Host ""

# --- Proje adi ---
if (-not $ProjectName) {
    if ($NonInteractive) { Write-Err $L.tuiNameRequired "E001"; exit 1 }
    $ProjectName = Read-Host $L.tuiProjectName
}
if (-not $ProjectName -or $ProjectName -match '[\s\\/]') {
    Write-Err $L.tuiInvalidName "E001"
    return
}


# --- Platform secimi ---
if (-not $Platform) {
    if ($NonInteractive) { $Platform = "Web" }
    else {
        $options = @(
            "Web App Only (React Vite/Next.js)",
            "Mobile App Only (React Native / Expo)",
            "Both Web & Mobile Monorepo"
        )
        $choice = Invoke-TuiMenu -Title $L.tuiSelectPlatform -Options $options
        $Platform = switch ($choice) { 0 { "Web" } 1 { "Mobile" } 2 { "Both" } }
    }
}

# --- Architecture secimi ---
if (-not $Architecture) {
    if ($NonInteractive) { $Architecture = "Layered" }
    else {
        $options = @(
            "Layered Architecture (Traditional Clean Arch)",
            "Feature-Sliced Design (FSD Experimental)"
        )
        $choice = Invoke-TuiMenu -Title $L.tuiSelectArch -Options $options
        $Architecture = switch ($choice) { 0 { "Layered" } 1 { "FeatureSliced" } }
    }
}

# --- ORM secimi ---
if (-not $OrmMode) {
    if ($NonInteractive) { $OrmMode = "EFCore" }
    else {
        $options = @(
            "Dapper Only (micro-ORM, raw SQL)",
            "EF Core Only (code-first, migrations)",
            "Hybrid (Yaz: Dapper | Oku: EF Core)"
        )
        $choice = Invoke-TuiMenu -Title $L.tuiSelectOrm -Options $options
        $OrmMode = switch ($choice) { 0 { "Dapper" } 1 { "EFCore" } 2 { "Hybrid" } }
    }
}

# --- Database secimi ---
if (-not $DbProvider) {
    if ($NonInteractive) { $DbProvider = "SQLite" }
    else {
        $options = @("MS SQL Server", "PostgreSQL", "SQLite")
        $choice = Invoke-TuiMenu -Title $L.tuiSelectDb -Options $options
        $DbProvider = switch ($choice) { 0 { "MSSQL" } 1 { "Postgres" } 2 { "SQLite" } }
    }
}

# --- Connection String secimi ---
if (-not $ConnectionString) {
    if ($NonInteractive) { $ConnectionString = "" }
    else {
        $options = @(
            "Varsayilan degerleri kullan (Lokal / Docker uyumlu)",
            "Kendi baglanti dizemi girmek istiyorum"
        )
        $choice = Invoke-TuiMenu -Title "Baglanti Dizesi (Connection String) Secin:" -Options $options
        if ($choice -eq 1) {
            Write-Host ""
            $ConnectionString = Read-Host "Lutfen veritabanı baglanti dizesini girin"
            Write-Host ""
        }
    }
}

# --- Frontend framework ---
if (-not $FrontendMode -and -not $SkipFrontend -and $Platform -ne "Mobile") {
    if ($NonInteractive) { $FrontendMode = "ReactTS" }
    else {
        $options = @(
            "React + Vite (TypeScript)",
            "React + Vite (JavaScript)",
            "Next.js (App Router + TypeScript)"
        )
        $choice = Invoke-TuiMenu -Title $L.tuiSelectFrontend -Options $options
        $FrontendMode = switch ($choice) { 0 { "ReactTS" } 1 { "ReactJS" } 2 { "NextJS" } }
    }
}

# --- CI Provider ---
if ($CiProvider -eq "GitHub" -and -not $SkipCi -and -not $NonInteractive) {
    # Eğer parametre ile verilmediyse (varsayılan GitHub olduğu için kontrolü parametre verilip verilmediğini anlamak için bazen zor olabilir,
    # ancak burada kullanıcıya sormak iyi bir UX'tir)
    $options = @("GitHub Actions", "GitLab CI", "None (Skip CI)")
    $choice = Invoke-TuiMenu -Title $L.tuiSelectCiCd -Options $options
    $CiProvider = switch ($choice) { 0 { "GitHub" } 1 { "GitLab" } 2 { "None" } }
    if ($CiProvider -eq "None") { $SkipCi = $true }
}

# --- Entities string split (virgülle geçilen "Product,Order" -> @("Product","Order")) ---
if ($Entities.Count -eq 1 -and $Entities[0] -match ',') {
    $Entities = $Entities[0] -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne '' }
}

# --- 'User' entity'si Auth icin zorunludur ---
if ($Entities -notcontains "User") { $Entities += "User" }

# --- Cikti koku ---
if (-not $OutputRootPath) {
    $currentDir = (Get-Location).Path
    if ($NonInteractive) { $OutputRootPath = $currentDir }
    else {
        Write-Host ""
        Write-Host ($L.tuiProjectDir -f $currentDir) -ForegroundColor DarkGray
        $inputRoot = Read-Host $L.tuiDirPrompt
        $OutputRootPath = if ($inputRoot.Trim()) { $inputRoot.Trim() } else { $currentDir }
    }
}

if (-not $Global:DryRun -and -not (Test-Path $OutputRootPath)) {
    New-Item -ItemType Directory -Path $OutputRootPath -Force | Out-Null
}
$OutputRootPath = [System.IO.Path]::GetFullPath($OutputRootPath)
$OutputPath = Join-Path $OutputRootPath $ProjectName

if (-not $Global:DryRun -and (Test-Path $OutputPath)) {
    Write-Err ($L.tuiDirExists -f $OutputPath) "E002"
    return
}


# Params Splatting
$Params = @{
    ProjectName   = $ProjectName
    OrmMode       = $OrmMode
    DbProvider    = $DbProvider
    OutputPath    = $OutputPath
    FrontendMode  = $FrontendMode
    SkipFrontend  = $SkipFrontend
    SkipAiGuide   = $SkipAiGuide
    SkipTests     = $SkipTests
    SkipCi        = $SkipCi
    CiProvider    = $CiProvider
    DryRun        = $DryRun
    Entities      = $Entities
    IdeConfig     = $IdeConfig
    Observability = $Observability
    I18n          = $I18n
    Language      = $Language
    Platform      = $Platform
    Architecture  = $Architecture
    MobileTooling    = $MobileTooling
    SkipNpm          = $SkipNpm
    ConnectionString = $ConnectionString
}

# --- Adimlar ---
& "$ModulesDir\core\Test-Prerequisites.ps1" -FrontendMode $FrontendMode
if ($LASTEXITCODE -ne 0) { exit 1 }

try {
    Invoke-Step $L.logCreatingSolution { & "$ModulesDir\solution\Initialize-Solution.ps1" @Params }
    Invoke-Step $L.logInstallingNuget { & "$ModulesDir\solution\Install-Packages.ps1" @Params }
    Invoke-Step $L.logDomain { & "$ModulesDir\backend\Write-DomainLayer.ps1" @Params }
    Invoke-Step $L.logApplication { & "$ModulesDir\backend\Write-ApplicationLayer.ps1" @Params }
    Invoke-Step $L.logInfrastructure { & "$ModulesDir\backend\Write-InfrastructureLayer.ps1" @Params }

    Invoke-Step $L.logPersistence {
        & "$ModulesDir\backend\Write-Persistence-Shared.ps1" @Params
        & "$ModulesDir\backend\Write-Persistence-Dapper.ps1" @Params
        & "$ModulesDir\backend\Write-Persistence-EFCore.ps1" @Params
        & "$ModulesDir\backend\Write-Persistence-Hybrid.ps1" @Params
    }

    Invoke-Step $L.logWebApi { & "$ModulesDir\backend\Write-WebApiLayer.ps1" @Params }

    if (-not $SkipTests) {
        Invoke-Step $L.logTests { & "$ModulesDir\testing\Write-TestLayer.ps1" @Params }
    }

    if (-not $SkipFrontend) {
        Invoke-Step $L.logFrontendSetup { & "$ModulesDir\solution\Install-Frontend.ps1" @Params }
        Invoke-Step $L.logFrontendFiles { & "$ModulesDir\frontend\Write-Frontend.ps1" @Params }
    }

    Invoke-Step $L.logDocker { & "$ModulesDir\devops\Write-Docker.ps1" @Params }

    if ($IdeConfig -ne "None") {
        Invoke-Step "$IdeConfig configuration" { & "$ModulesDir\devops\Write-IdeConfig.ps1" @Params }
    }

    if (-not $SkipCi) {
        Invoke-Step $L.logCiCd { & "$ModulesDir\devops\Write-CiPipeline.ps1" @Params }
    }

    Invoke-Step $L.logMigrations { & "$ModulesDir\devops\Write-MigrationScript.ps1" @Params }
    Invoke-Step $L.logReadme { & "$ModulesDir\solution\Write-Readme.ps1" @Params }

    if (-not $SkipAiGuide) {
        Invoke-Step $L.logAiGuide {
            & "$ModulesDir\ai-guide\Write-AiGuide-Core.ps1" @Params
            & "$ModulesDir\ai-guide\Write-AiGuide-Endpoints.ps1" @Params
            & "$ModulesDir\ai-guide\Write-AiGuide-Frontend.ps1" @Params
        }
    }

    # Summary
    Write-Host ""
    Write-Host "================================================" -ForegroundColor Green
    Write-Host (" " + ($L.tuiReady -f $ProjectName)) -ForegroundColor Green
    Write-Host (" " + ($L.tuiTotalTime -f $TotalSw.Elapsed.TotalSeconds)) -ForegroundColor Green
    Write-Host "================================================" -ForegroundColor Green
    Write-Host ""
    Write-Host "$($L.tuiLocation) : $OutputPath"
    Write-Host "ORM : $OrmMode"
    Write-Host "Database : $DbProvider"
    Write-Host "Frontend : $(if($SkipFrontend){'None'}else{$FrontendMode})"
    Write-Host "IDE : $IdeConfig"
    Write-Host ""

} catch {
    Write-Err ($L.tuiErrorOccurred -f $_) "E003"
    if (-not $Global:DryRun) {
        Write-Warn $L.tuiCleaningUp
        Remove-Item $OutputPath -Recurse -Force -ErrorAction SilentlyContinue
    }
    return
}