[CmdletBinding()]
param(
    [Parameter(Mandatory=$false)]
    [string]$ProjectPath
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$script:LoaderRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$script:ConfigPath = Join-Path -Path $script:LoaderRoot -ChildPath 'config.json'
$script:ConfigCache = $null

function Get-ProjectStructureConfig {
    if ($null -ne $script:ConfigCache) {
        return $script:ConfigCache
    }

    if (Test-Path -LiteralPath $script:ConfigPath) {
        try {
            $raw = Get-Content -LiteralPath $script:ConfigPath -Raw -ErrorAction Stop
            if ($raw.Trim().Length -gt 0) {
                $script:ConfigCache = $raw | ConvertFrom-Json
                return $script:ConfigCache
            }
        }
        catch {
            Write-Warning "Failed to parse config.json: $($_.Exception.Message)"
        }
    }

    $script:ConfigCache = [pscustomobject]@{}
    return $script:ConfigCache
}

function Get-ConfigValue {
    param(
        [Parameter(Mandatory)] [string]$Name,
        $Default = $null
    )

    $config = Get-ProjectStructureConfig
    if ($config.PSObject.Properties.Name -contains $Name) {
        $value = $config.$Name
        if ($null -ne $value -and ($value -isnot [string] -or $value.Trim().Length -gt 0)) {
            return $value
        }
    }

    return $Default
}

function Get-StructDirectory {
    # When script is run directly (not dot-sourced), always use script's directory for portability
    if ($MyInvocation.InvocationName -ne '.') {
        return $script:LoaderRoot
    }

    $value = Get-ConfigValue -Name 'structDir'
    if ($null -eq $value -or [string]::IsNullOrWhiteSpace($value)) {
        return $script:LoaderRoot
    }

    if ([System.IO.Path]::IsPathRooted($value)) {
        $resolved = Resolve-Path -LiteralPath $value -ErrorAction SilentlyContinue
        if ($null -ne $resolved) { return $resolved.Path }
        return $value
    }

    $candidate = Join-Path -Path $script:LoaderRoot -ChildPath $value
    $resolvedCandidate = Resolve-Path -LiteralPath $candidate -ErrorAction SilentlyContinue
    if ($null -ne $resolvedCandidate) { return $resolvedCandidate.Path }
    return $candidate
}

function Get-ProjectsRoot {
    $value = Get-ConfigValue -Name 'projectsRoot'
    if ($null -eq $value -or [string]::IsNullOrWhiteSpace($value)) {
        $structDir = Get-StructDirectory
        $parent = Split-Path -Parent $structDir
        if ($null -eq $parent -or $parent.Length -eq 0 -or $parent -eq $structDir) {
            return $structDir
        }
        return $parent
    }

    if ([System.IO.Path]::IsPathRooted($value)) {
        $resolved = Resolve-Path -LiteralPath $value -ErrorAction SilentlyContinue
        if ($null -ne $resolved) { return $resolved.Path }
        return $value
    }

    $candidate = Join-Path -Path $script:LoaderRoot -ChildPath $value
    $resolvedCandidate = Resolve-Path -LiteralPath $candidate -ErrorAction SilentlyContinue
    if ($null -ne $resolvedCandidate) { return $resolvedCandidate.Path }
    return $candidate
}

function Get-ProjectPathFromUser {
    param(
        [string]$Prompt = "Paste the project path to deploy in"
    )

    do {
        $inputPath = Read-Host -Prompt $Prompt
        if ([string]::IsNullOrWhiteSpace($inputPath)) {
            Write-Warning "Path cannot be empty. Please try again."
            continue
        }

        # Remove quotes if present
        $inputPath = $inputPath.Trim('"', "'")

        # Try to resolve the path
        try {
            if (Test-Path -LiteralPath $inputPath -PathType Container) {
                $resolved = Resolve-Path -LiteralPath $inputPath -ErrorAction Stop
                return $resolved.Path
            }
            else {
                Write-Warning "Path does not exist or is not a directory: $inputPath"
                $retry = Read-Host "Try again? (Y/N)"
                if ($retry -notmatch '^[Yy]') {
                    return $null
                }
            }
        }
        catch {
            Write-Warning "Invalid path: $($_.Exception.Message)"
            $retry = Read-Host "Try again? (Y/N)"
            if ($retry -notmatch '^[Yy]') {
                return $null
            }
        }
    } while ($true)
}

function Use-IsoDailyFormat {
    $dailyFormat = Get-ConfigValue -Name 'dailyFormat' -Default $true
    
    # Handle backward compatibility with boolean values
    if ($dailyFormat -is [bool]) {
        return $dailyFormat
    }
    
    # Handle string values
    if ($dailyFormat -is [string]) {
        $formatStr = $dailyFormat.Trim()
        # ISO format contains YYYY-MM-DD pattern
        if ($formatStr -match 'YYYY-MM-DD' -or $formatStr -match '\d{4}-\d{2}-\d{2}') {
            return $true
        }
        # daily_YYMMDD or other formats are not ISO
        return $false
    }
    
    # Default to false (not ISO format) for unknown types
    return $false
}

function Use-7Zip {
    $zipper = Get-ConfigValue -Name 'zipper' -Default $true
    
    # Handle backward compatibility with boolean values
    if ($zipper -is [bool]) {
        return $zipper
    }
    
    # Handle string values
    if ($zipper -is [string]) {
        $zipperStr = $zipper.Trim().ToLower()
        return ($zipperStr -eq '7z')
    }
    
    # Default to false (use zip) for unknown types
    return $false
}

function Get-ZipCompressionLevel {
    $value = Get-ConfigValue -Name 'compression' -Default 9
    if ($value -is [string]) {
        $parsed = 0
        if ([int]::TryParse($value, [ref]$parsed)) {
            $value = $parsed
        }
    }

    if ($value -isnot [int]) {
        return 9
    }

    return [Math]::Min(9, [Math]::Max(0, $value))
}

function Update-ConfigStructDir {
    param(
        [Parameter(Mandatory)] [string]$StructDir
    )

    $config = Get-ProjectStructureConfig
    $config.structDir = $StructDir
    
    try {
        $json = $config | ConvertTo-Json -Depth 10
        Set-Content -LiteralPath $script:ConfigPath -Value $json -NoNewline -ErrorAction Stop
        $script:ConfigCache = $config
    }
    catch {
        Write-Warning "Failed to update config.json: $($_.Exception.Message)"
    }
}

# If script is run directly (not dot-sourced), prompt for project path and deploy
# When dot-sourced, InvocationName is '.'; when run directly, it's the script path or name
if ($MyInvocation.InvocationName -ne '.') {
    # Update config.json to use script's directory as structDir for portability
    Update-ConfigStructDir -StructDir $script:LoaderRoot
    
    # Use provided ProjectPath parameter, or prompt user if not provided
    if ([string]::IsNullOrWhiteSpace($ProjectPath)) {
        $projectPath = Get-ProjectPathFromUser
    }
    else {
        # Remove quotes if present and resolve the provided path
        $ProjectPath = $ProjectPath.Trim('"', "'")
        try {
            if (Test-Path -LiteralPath $ProjectPath -PathType Container) {
                $projectPath = (Resolve-Path -LiteralPath $ProjectPath -ErrorAction Stop).Path
            }
            else {
                Write-Error "Project path does not exist or is not a directory: $ProjectPath"
                exit 1
            }
        }
        catch {
            Write-Error "Unable to resolve project directory: $($_.Exception.Message)"
            exit 1
        }
    }
    
    if ($null -ne $projectPath) {
        # Deploy batch files and config to the project
        $structDir = Get-StructDirectory
        if (-not (Test-Path -LiteralPath $structDir -PathType Container)) {
            Write-Error "Configured structDir not found: $structDir"
            exit 1
        }

        try {
            $resolvedProject = (Resolve-Path -LiteralPath $projectPath -ErrorAction Stop).Path
        }
        catch {
            Write-Error "Unable to resolve project directory: $($_.Exception.Message)"
            exit 1
        }

        if (-not (Test-Path -LiteralPath $resolvedProject -PathType Container)) {
            Write-Error "Project path is not a directory: $resolvedProject"
            exit 1
        }

        Write-Host "`nDeploying to: $resolvedProject" -ForegroundColor Cyan
        Write-Host "Struct directory:  $structDir" -ForegroundColor Cyan

        $specs = @(
            @{ Name = 'UpdateSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateSequences.bat' },
            @{ Name = 'UpdateAllSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateAllSequences.bat' },
            @{ Name = 'ZipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'ZipSeqArchv.bat' },
            @{ Name = 'UnzipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'UnzipSeqArchv.bat' },
            @{ Name = 'NewDaily.bat'; Source = Join-Path -Path $structDir -ChildPath 'NewDaily.bat' },
            @{ Name = '.gitattributes'; Source = Join-Path -Path $structDir -ChildPath 'components\gitattributes' },
            @{ Name = '.gitignore'; Source = Join-Path -Path $structDir -ChildPath 'components\gitignore' }
        )

        # Config files to deploy to projectroot\.config\
        $configAssets = @(
            @{ Name = 'config.json'; Source = Join-Path -Path $structDir -ChildPath 'config.json' },
            @{ Name = 'GetStructDir.ps1'; Source = Join-Path -Path $structDir -ChildPath 'GetStructDir.ps1' }
        )

        foreach ($spec in $specs) {
            if (-not (Test-Path -LiteralPath $spec.Source -PathType Leaf)) {
                Write-Error "Source file not found: $($spec.Source)"
                exit 1
            }
        }

        foreach ($asset in $configAssets) {
            if (-not (Test-Path -LiteralPath $asset.Source -PathType Leaf)) {
                Write-Error "Config asset not found: $($asset.Source)"
                exit 1
            }
        }

        # Ensure .config directory exists in project root and is hidden
        $projectConfigDir = Join-Path -Path $resolvedProject -ChildPath '.config'
        if (-not (Test-Path -LiteralPath $projectConfigDir -PathType Container)) {
            New-Item -Path $projectConfigDir -ItemType Directory -Force | Out-Null
            Write-Host "Created .config directory: $projectConfigDir" -ForegroundColor Cyan
        }
        # Set hidden attribute on .config directory
        $folder = Get-Item -LiteralPath $projectConfigDir -Force
        $folder.Attributes = $folder.Attributes -bor [System.IO.FileAttributes]::Hidden

        $touchedDirs = @{}
        $summary = @()

        foreach ($spec in $specs) {
            Write-Host "`n=== Updating $($spec.Name) ===" -ForegroundColor Magenta

            $targets = Get-ChildItem -LiteralPath $resolvedProject -Recurse -Filter $spec.Name -File -Force -ErrorAction SilentlyContinue
            $targets = $targets | Where-Object { $_.FullName -ne $spec.Source }

            if (-not $targets) {
                Write-Host "No targets found." -ForegroundColor Yellow
                $summary += [pscustomobject]@{
                    Name     = $spec.Name
                    Updated  = 0
                    Failed   = 0
                    Skipped  = 0
                    Total    = 0
                }
                continue
            }

            $updated = 0
            $failed = 0

            foreach ($target in $targets) {
                try {
                    Copy-Item -Path $spec.Source -Destination $target.FullName -Force
                    # Set hidden attribute for .gitattributes and .gitignore files
                    if ($spec.Name -eq '.gitattributes' -or $spec.Name -eq '.gitignore') {
                        $file = Get-Item -LiteralPath $target.FullName -Force
                        $file.Attributes = $file.Attributes -bor [System.IO.FileAttributes]::Hidden
                    }
                    Write-Host "[OK] $($target.FullName)" -ForegroundColor Green
                    $updated++

                    $targetDir = $target.Directory.FullName
                    if (-not $touchedDirs.ContainsKey($targetDir)) {
                        $touchedDirs[$targetDir] = $true
                    }
                }
                catch {
                    Write-Host "[FAIL] $($target.FullName)" -ForegroundColor Red
                    Write-Host "  $($_.Exception.Message)" -ForegroundColor DarkRed
                    $failed++
                }
            }

            $summary += [pscustomobject]@{
                Name     = $spec.Name
                Updated  = $updated
                Failed   = $failed
                Skipped  = 0
                Total    = @($targets).Count
            }
        }

        Write-Host "`n=== Summary ===" -ForegroundColor Cyan
        foreach ($item in $summary) {
            Write-Host ("{0,-22} Updated: {1,3}  Failed: {2,3}  Total: {3,3}" -f $item.Name, $item.Updated, $item.Failed, $item.Total)
        }

        if (($summary | Measure-Object -Property Failed -Sum).Sum -gt 0) {
            Write-Host "Completed with errors." -ForegroundColor Yellow
            exit 1
        }

        Write-Host "All batch files refreshed successfully." -ForegroundColor Green

        # Deploy UpdateSequences.bat, ZipSeqArchv.bat, UnzipSeqArchv.bat, CompressPNGs.bat to \Renders
        Write-Host "`n=== Deploying Renders batch files to \Renders ===" -ForegroundColor Magenta
        $rendersDir = Join-Path -Path $resolvedProject -ChildPath 'Renders'
        if (-not (Test-Path -LiteralPath $rendersDir -PathType Container)) {
            New-Item -Path $rendersDir -ItemType Directory -Force | Out-Null
            Write-Host "Created Renders directory: $rendersDir" -ForegroundColor Cyan
        }
        $rendersBatches = @(
            @{ Name = 'UpdateSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateSequences.bat' },
            @{ Name = 'ZipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'ZipSeqArchv.bat' },
            @{ Name = 'UnzipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'UnzipSeqArchv.bat' },
            @{ Name = 'CompressPNGs.bat'; Source = Join-Path -Path $structDir -ChildPath 'CompressPNGs.bat' }
        )
        foreach ($bat in $rendersBatches) {
            $targetPath = Join-Path -Path $rendersDir -ChildPath $bat.Name
            try {
                Copy-Item -Path $bat.Source -Destination $targetPath -Force
                Write-Host "[OK] $targetPath" -ForegroundColor Green
            }
            catch {
                Write-Host "[FAIL] $targetPath" -ForegroundColor Red
                Write-Host "  $($_.Exception.Message)" -ForegroundColor DarkRed
                exit 1
            }
        }

        # Deploy .gitattributes and .gitignore to project root if they don't exist
        Write-Host "`n=== Deploying .gitattributes and .gitignore to project root ===" -ForegroundColor Magenta
        $gitFiles = @(
            @{ Name = '.gitattributes'; Source = Join-Path -Path $structDir -ChildPath 'components\gitattributes' },
            @{ Name = '.gitignore'; Source = Join-Path -Path $structDir -ChildPath 'components\gitignore' }
        )
        foreach ($gitFile in $gitFiles) {
            $targetPath = Join-Path -Path $resolvedProject -ChildPath $gitFile.Name
            try {
                Copy-Item -Path $gitFile.Source -Destination $targetPath -Force
                # Set hidden attribute on the file
                $file = Get-Item -LiteralPath $targetPath -Force
                $file.Attributes = $file.Attributes -bor [System.IO.FileAttributes]::Hidden
                Write-Host "[OK] $targetPath" -ForegroundColor Green
            }
            catch {
                Write-Host "[FAIL] $targetPath" -ForegroundColor Red
                Write-Host "  $($_.Exception.Message)" -ForegroundColor DarkRed
                exit 1
            }
        }

        # Deploy config files to projectroot\.config\
        Write-Host "`n=== Deploying config files to .config\ ===" -ForegroundColor Magenta
        foreach ($asset in $configAssets) {
            $targetPath = Join-Path -Path $projectConfigDir -ChildPath $asset.Name
            try {
                Copy-Item -Path $asset.Source -Destination $targetPath -Force
                Write-Host "[OK] $targetPath" -ForegroundColor Green
            }
            catch {
                Write-Host "[FAIL] $targetPath" -ForegroundColor Red
                Write-Host "  $($_.Exception.Message)" -ForegroundColor DarkRed
                exit 1
            }
        }

        Write-Host "`nConfig files deployed successfully." -ForegroundColor Green
        Write-Host "Deployment completed successfully." -ForegroundColor Green
        exit 0
    }
    else {
        Write-Host "No project path provided." -ForegroundColor Yellow
        exit 1
    }
}
