modules/devops/Write-MigrationScript.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$DbProvider,
    [string]$OutputPath
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")

# Migration script sadece EFCore veya Hybrid modda üretilir
if ($OrmMode -notmatch "EFCore|Hybrid") { return }

$migratePs1Content = @'
#Requires -Version 5.1
# EF Core Migration Helper — {P}
# Kullanim: .\migrate.ps1 [-MigrationName <ad>] [-Revert]
 
param(
    [string]$MigrationName = "Initial",
    [switch]$Revert
)
 
# Self-healing dotnet-ef tool check
$dotnetEfInstalled = dotnet tool list -g | Select-String "dotnet-ef"
if (-not $dotnetEfInstalled) {
    Write-Host "dotnet-ef araci kuruluyor..." -ForegroundColor Yellow
    dotnet tool install --global dotnet-ef
}
$userProfile = [System.Environment]::GetFolderPath('UserProfile')
$globalToolsPath = Join-Path $userProfile ".dotnet\tools"
if ($env:PATH -notlike "*$globalToolsPath*") {
    $env:PATH = "$env:PATH;$globalToolsPath"
}
 
# Resolve relative paths using PSScriptRoot
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$dockerDir = Split-Path $scriptDir -Parent
$projectRootDir = Split-Path $dockerDir -Parent
$backendDir = Join-Path $projectRootDir "{P}-backend"
if (-not (Test-Path $backendDir)) { $backendDir = $projectRootDir }
 
$persistenceProj = Join-Path $backendDir "src\{P}.Persistence"
$startupProj = Join-Path $backendDir "src\{P}.WebApi"
 
if ($Revert) {
    Write-Host "Son migration geri aliniyor..." -ForegroundColor Yellow
    dotnet ef database update 0 `
        --project $persistenceProj `
        --startup-project $startupProj
    Write-Host "Tum migrasyonlar siliniyor..." -ForegroundColor Yellow
    dotnet ef migrations remove `
        --project $persistenceProj `
        --startup-project $startupProj
    Write-Host "Tamamlandi." -ForegroundColor Green
} else {
    Write-Host "Migration olusturuluyor: $MigrationName" -ForegroundColor Cyan
    dotnet ef migrations add $MigrationName `
        --project $persistenceProj `
        --startup-project $startupProj `
        --output-dir Migrations
 
    Write-Host "Veritabani guncelleniyor..." -ForegroundColor Cyan
    dotnet ef database update `
        --project $persistenceProj `
        --startup-project $startupProj
 
    Write-Host "Tamamlandi." -ForegroundColor Green
}
'@
.Replace("{P}", $ProjectName)

$migrateShContent = @'
#!/bin/bash
# EF Core Migration Helper — {P}
# Kullanim: ./migrate.sh [MigrationName] [--revert]
 
MIGRATION_NAME=${1:-Initial}
 
# Self-healing dotnet-ef check
if ! command -v dotnet-ef &> /dev/null
then
    echo "dotnet-ef araci kuruluyor..."
    dotnet tool install --global dotnet-ef
    export PATH="$PATH:$HOME/.dotnet/tools"
fi
 
# Resolve paths dynamically using BASH_SOURCE
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DOCKER_DIR="$( dirname "$SCRIPT_DIR" )"
PROJECT_ROOT_DIR="$( dirname "$DOCKER_DIR" )"
BACKEND_DIR="$PROJECT_ROOT_DIR/{P}-backend"
if [ ! -d "$BACKEND_DIR" ]; then
    BACKEND_DIR="$PROJECT_ROOT_DIR"
fi
PERSISTENCE_PROJ="$BACKEND_DIR/src/{P}.Persistence"
STARTUP_PROJ="$BACKEND_DIR/src/{P}.WebApi"
 
if [ "$2" = "--revert" ]; then
    echo "Son migration geri aliniyor..."
    dotnet ef database update 0 \
        --project "$PERSISTENCE_PROJ" \
        --startup-project "$STARTUP_PROJ"
    dotnet ef migrations remove \
        --project "$PERSISTENCE_PROJ" \
        --startup-project "$STARTUP_PROJ"
    echo "Tamamlandi."
else
    echo "Migration olusturuluyor: $MIGRATION_NAME"
    dotnet ef migrations add "$MIGRATION_NAME" \
        --project "$PERSISTENCE_PROJ" \
        --startup-project "$STARTUP_PROJ" \
        --output-dir Migrations
 
    echo "Veritabani guncelleniyor..."
    dotnet ef database update \
        --project "$PERSISTENCE_PROJ" \
        --startup-project "$STARTUP_PROJ"
 
    echo "Tamamlandi."
fi
'@
.Replace("{P}", $ProjectName)

$dbFolder = if ($DbProvider -eq "Postgres") { "postgresql" } else { "sqlserver" }
$targetDir = Join-Path $OutputPath "$ProjectName-docker\$dbFolder"
if (-not $Global:DryRun -and -not (Test-Path $targetDir)) {
    New-Item -ItemType Directory -Path $targetDir -Force | Out-Null
}

Write-CoreFile (Join-Path $targetDir "migrate.ps1") $migratePs1Content
Write-CoreFile (Join-Path $targetDir "migrate.sh") $migrateShContent

# Automatic Initial Migration Generation for EFCore & Hybrid modes
if (-not $Global:DryRun) {
    $userProfile = [System.Environment]::GetFolderPath('UserProfile')
    $globalToolsPath = Join-Path $userProfile ".dotnet\tools"
    if ($env:PATH -notlike "*$globalToolsPath*") {
        $env:PATH = "$env:PATH;$globalToolsPath"
    }

    $efCheck = Get-Command "dotnet-ef" -ErrorAction SilentlyContinue
    if (-not $efCheck) {
        try {
            dotnet tool install --global dotnet-ef --erroraction SilentlyContinue | Out-Null
        } catch {}
    }

    $backendDir = Join-Path $OutputPath "$ProjectName-backend"
    if (-not (Test-Path $backendDir)) { $backendDir = $OutputPath }

    $persistenceProjPath = Join-Path $backendDir "src\$ProjectName.Persistence\$ProjectName.Persistence.csproj"
    $webApiProjPath      = Join-Path $backendDir "src\$ProjectName.WebApi\$ProjectName.WebApi.csproj"
    $migrationsDir       = Join-Path $backendDir "src\$ProjectName.Persistence\Migrations"

    $migrationSuccess = $false
    try {
        dotnet restore $webApiProjPath --nologo -v q | Out-Null
        dotnet build $webApiProjPath -c Debug --nologo -v q | Out-Null
        $res = dotnet ef migrations add InitialCreate --project $persistenceProjPath --startup-project $webApiProjPath --output-dir Migrations 2>&1
        $createdFiles = Get-ChildItem -Path $migrationsDir -Filter "*InitialCreate*.cs" -ErrorAction SilentlyContinue
        if ($createdFiles -and $createdFiles.Count -gt 0) {
            $migrationSuccess = $true
        }
    } catch {}

    # Static fallback generation if dotnet-ef tool failed or unavailable
    if (-not $migrationSuccess) {
        if (-not (Test-Path $migrationsDir)) {
            New-Item -ItemType Directory -Path $migrationsDir -Force | Out-Null
        }

        $snapshotContent = @'
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using {P}.Persistence.Context;
 
#nullable disable
 
namespace {P}.Persistence.Migrations
{
    [DbContext(typeof(AppDbContext))]
    partial class AppDbContextModelSnapshot : ModelSnapshot
    {
        protected override void BuildModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "10.0.0");
 
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
#pragma warning restore 612, 618
        }
    }
}
'@
.Replace("{P}", $ProjectName)

        $initialCreateContent = @'
using System;
using Microsoft.EntityFrameworkCore.Migrations;
 
#nullable disable
 
namespace {P}.Persistence.Migrations
{
    /// <inheritdoc />
    public partial class InitialCreate : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Users",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Username = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
                    PasswordHash = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
                    Email = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: true),
                    Role = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false, defaultValue: "User"),
                    IsActive = table.Column<bool>(type: "bit", nullable: false, defaultValue: true),
                    IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
                    DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
                    CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Users", x => x.Id);
                });
 
            migrationBuilder.CreateTable(
                name: "Products",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Name = table.Column<string>(type: "nvarchar(200)", maxLength: 200, nullable: false),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false, defaultValue: 0m),
                    IsDeleted = table.Column<bool>(type: "bit", nullable: false, defaultValue: false),
                    DeletedAt = table.Column<DateTime>(type: "datetime2", nullable: true),
                    CreatedAt = table.Column<DateTime>(type: "datetime2", nullable: false),
                    UpdatedAt = table.Column<DateTime>(type: "datetime2", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Products", x => x.Id);
                });
        }
 
        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(name: "Products");
            migrationBuilder.DropTable(name: "Users");
        }
    }
}
'@
.Replace("{P}", $ProjectName)

        $designerContent = @'
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using {P}.Persistence.Context;
 
#nullable disable
 
namespace {P}.Persistence.Migrations
{
    [DbContext(typeof(AppDbContext))]
    [Migration("20260101000000_InitialCreate")]
    partial class InitialCreate
    {
        /// <inheritdoc />
        protected override void BuildTargetModel(ModelBuilder modelBuilder)
        {
#pragma warning disable 612, 618
            modelBuilder
                .HasAnnotation("ProductVersion", "10.0.0");
 
            modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
#pragma warning restore 612, 618
        }
    }
}
'@
.Replace("{P}", $ProjectName)

        Write-CoreFile (Join-Path $migrationsDir "AppDbContextModelSnapshot.cs") $snapshotContent
        Write-CoreFile (Join-Path $migrationsDir "20260101000000_InitialCreate.cs") $initialCreateContent
        Write-CoreFile (Join-Path $migrationsDir "20260101000000_InitialCreate.Designer.cs") $designerContent
    }
}