@echo off
setlocal
chcp 65001 >nul 2>&1
set "BKIT_SELF=%~f0"
set "BKIT_ARG1=%~1"
set "BKIT_ARG2=%~2"
set "BKIT_ARG3=%~3"
set "BKIT_ARG4=%~4"
set "BKIT_ARG5=%~5"
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "$m='#<'+'BKIT_POWERSHELL'+'>'; $c=[IO.File]::ReadAllText($env:BKIT_SELF); $i=$c.IndexOf($m); if($i -lt 0){throw 'Khong tim thay PowerShell payload.'}; & ([ScriptBlock]::Create($c.Substring($i+$m.Length)))"
set "BKIT_EXIT=%ERRORLEVEL%"
endlocal & exit /b %BKIT_EXIT%
#<BKIT_POWERSHELL>

$ErrorActionPreference = "Stop"
$utf8 = New-Object System.Text.UTF8Encoding($false)
[Console]::InputEncoding = $utf8
[Console]::OutputEncoding = $utf8
$OutputEncoding = $utf8

$PackageRoot = Split-Path -Parent $env:BKIT_SELF
$ManifestPath = Join-Path $PackageRoot "provider-manifest.json"
$InstallProviderIds = @(
    "openai-codex",
    "anthropic-claude-code",
    "github-copilot",
    "google-gemini-cli",
    "cursor",
    "cline",
    "windsurf",
    "kilo-code",
    "opencode"
)

$ProviderAliases = @{
    "openai" = "openai-codex"
    "codex" = "openai-codex"
    "openai-codex" = "openai-codex"
    "anthropic" = "anthropic-claude-code"
    "claude" = "anthropic-claude-code"
    "claude-code" = "anthropic-claude-code"
    "anthropic-claude-code" = "anthropic-claude-code"
    "github" = "github-copilot"
    "copilot" = "github-copilot"
    "github-copilot" = "github-copilot"
    "google" = "google-gemini-cli"
    "gemini" = "google-gemini-cli"
    "gemini-cli" = "google-gemini-cli"
    "google-gemini-cli" = "google-gemini-cli"
    "cursor" = "cursor"
    "cline" = "cline"
    "windsurf" = "windsurf"
    "kilo" = "kilo-code"
    "kilo-code" = "kilo-code"
    "opencode" = "opencode"
    "standard" = "agent-skills-standard"
    "agents" = "agent-skills-standard"
    "agent-skills-standard" = "agent-skills-standard"
}

$UserSkillRoots = @{
    "openai-codex" = ".agents\skills"
    "anthropic-claude-code" = ".claude\skills"
    "github-copilot" = ".copilot\skills"
    "google-gemini-cli" = ".gemini\skills"
    "cursor" = ".cursor\skills"
    "cline" = ".cline\skills"
    "windsurf" = ".codeium\windsurf\skills"
    "kilo-code" = ".kilo\skills"
    "opencode" = ".config\opencode\skills"
    "agent-skills-standard" = ".agents\skills"
}

$ProjectSkillRoots = @{
    "openai-codex" = ".agents\skills"
    "anthropic-claude-code" = ".claude\skills"
    "github-copilot" = ".github\skills"
    "google-gemini-cli" = ".gemini\skills"
    "cursor" = ".cursor\skills"
    "cline" = ".cline\skills"
    "windsurf" = ".windsurf\skills"
    "kilo-code" = ".kilo\skills"
    "opencode" = ".opencode\skills"
    "agent-skills-standard" = ".agents\skills"
}

function Write-Banner {
    Write-Host ""
    Write-Host "BKIT AI Agent Skills" -ForegroundColor Cyan
    Write-Host "40 quy trinh BKIT cho 9 AI agent va chuan Agent Skills."
    Write-Host ""
}

function Get-Manifest {
    if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) {
        throw "Khong tim thay provider-manifest.json ben canh bkit.bat."
    }

    return Get-Content -Raw -LiteralPath $ManifestPath -Encoding UTF8 | ConvertFrom-Json
}

function Get-Provider {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProviderId
    )

    $provider = $script:Manifest.providers | Where-Object { $_.id -eq $ProviderId } | Select-Object -First 1
    if ($null -eq $provider) {
        throw "Provider khong ton tai trong manifest: $ProviderId"
    }
    return $provider
}

function Resolve-ProviderIds {
    param([string]$InputName)

    $normalized = if ([string]::IsNullOrWhiteSpace($InputName)) {
        "all"
    } else {
        $InputName.Trim().ToLowerInvariant()
    }

    if ($normalized -eq "all") {
        return $InstallProviderIds
    }

    if (-not $ProviderAliases.ContainsKey($normalized)) {
        throw "Provider '$InputName' khong hop le. Chay 'bkit.bat list' de xem danh sach."
    }

    return @($ProviderAliases[$normalized])
}

function Get-DestinationRoot {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProviderId,
        [Parameter(Mandatory = $true)]
        [ValidateSet("user", "project")]
        [string]$Scope,
        [string]$ProjectPath
    )

    if ($Scope -eq "user") {
        if ([string]::IsNullOrWhiteSpace($env:USERPROFILE)) {
            throw "Khong xac dinh duoc USERPROFILE."
        }
        return [IO.Path]::GetFullPath((Join-Path $env:USERPROFILE $UserSkillRoots[$ProviderId]))
    }

    if ([string]::IsNullOrWhiteSpace($ProjectPath)) {
        throw "Can duong dan project khi scope la project."
    }

    $projectRoot = [IO.Path]::GetFullPath($ProjectPath)
    return [IO.Path]::GetFullPath((Join-Path $projectRoot $ProjectSkillRoots[$ProviderId]))
}

function Test-FrontmatterName {
    param(
        [Parameter(Mandatory = $true)]
        [string]$SkillPath,
        [Parameter(Mandatory = $true)]
        [string]$ExpectedName
    )

    $content = Get-Content -Raw -LiteralPath $SkillPath -Encoding UTF8
    $match = [regex]::Match($content, "(?m)^name:\s*([a-z0-9-]+)\s*$")
    if (-not $match.Success -or $match.Groups[1].Value.Trim() -ne $ExpectedName) {
        throw "Frontmatter name khong khop: $SkillPath"
    }
    return $content
}

function Test-Package {
    param([switch]$Quiet)

    $expectedCount = [int]$script:Manifest.skillCount
    if ($expectedCount -le 0) {
        throw "skillCount trong manifest khong hop le."
    }
    if ($script:Manifest.version -notmatch '^\d{4}_\d{2}_\d{2}__\d{2}_\d{2}_\d{2}$') {
        throw "version phai co dinh dang YYYY_MM_DD__HH_mm_ss."
    }
    if ($script:Manifest.sourceCommit -notmatch '^[0-9a-fA-F]{40}$') {
        throw "sourceCommit phai la Git commit hash day du 40 ky tu."
    }

    $canonical = @{}
    $standard = Get-Provider "agent-skills-standard"
    $standardRoot = Join-Path (Join-Path $PackageRoot $standard.id) $standard.nativeSkillRoot

    foreach ($directory in Get-ChildItem -LiteralPath $standardRoot -Directory | Sort-Object Name) {
        $skillPath = Join-Path $directory.FullName "SKILL.md"
        if (-not (Test-Path -LiteralPath $skillPath -PathType Leaf)) {
            throw "Thieu SKILL.md: $($directory.FullName)"
        }
        $canonical[$directory.Name] = Test-FrontmatterName $skillPath $directory.Name
    }

    if ($canonical.Count -ne $expectedCount) {
        throw "Goi chuan co $($canonical.Count) skills, mong doi $expectedCount."
    }

    foreach ($provider in $script:Manifest.providers) {
        $providerRoot = Join-Path $PackageRoot $provider.id
        $providerMetadataPath = Join-Path $providerRoot "provider.json"
        if (-not (Test-Path -LiteralPath $providerMetadataPath -PathType Leaf)) {
            throw "Thieu provider.json: $($provider.id)"
        }

        $providerMetadata = Get-Content -Raw -LiteralPath $providerMetadataPath -Encoding UTF8 | ConvertFrom-Json
        if ($providerMetadata.version -ne $script:Manifest.version) {
            throw "Version khong khop: $($provider.id)"
        }
        if ($providerMetadata.sourceCommit -ne $script:Manifest.sourceCommit) {
            throw "Source commit khong khop: $($provider.id)"
        }
        if ($providerMetadata.contentDigest -ne $script:Manifest.contentDigest) {
            throw "Content digest khong khop: $($provider.id)"
        }

        $skillsRoot = Join-Path $providerRoot $provider.nativeSkillRoot
        $skillDirectories = @(Get-ChildItem -LiteralPath $skillsRoot -Directory | Sort-Object Name)
        if ($skillDirectories.Count -ne $expectedCount) {
            throw "$($provider.id) co $($skillDirectories.Count) skills, mong doi $expectedCount."
        }

        foreach ($directory in $skillDirectories) {
            $skillPath = Join-Path $directory.FullName "SKILL.md"
            if (-not $canonical.ContainsKey($directory.Name)) {
                throw "Skill khong co trong goi chuan: $($provider.id)/$($directory.Name)"
            }
            $content = Test-FrontmatterName $skillPath $directory.Name
            if ($content -ne $canonical[$directory.Name]) {
                throw "Noi dung skill bi lech: $($provider.id)/$($directory.Name)"
            }
        }

        if ([bool]$provider.includeOpenaiMetadata) {
            $metadataCount = @(
                Get-ChildItem -LiteralPath $skillsRoot -Recurse -File -Filter "openai.yaml"
            ).Count
            if ($metadataCount -ne $expectedCount) {
                throw "$($provider.id) co $metadataCount openai.yaml, mong doi $expectedCount."
            }
        }
    }

    $geminiManifest = Join-Path $PackageRoot "google-gemini-cli\gemini-extension.json"
    if (-not (Test-Path -LiteralPath $geminiManifest -PathType Leaf)) {
        throw "Thieu gemini-extension.json."
    }

    if (-not $Quiet) {
        Write-Host "PASS: $($script:Manifest.providers.Count) provider packs, $expectedCount skills/goi, $($script:Manifest.providers.Count * $expectedCount) SKILL.md." -ForegroundColor Green
        Write-Host "Version: $($script:Manifest.version)"
        Write-Host "Source commit: $($script:Manifest.sourceCommit)"
        Write-Host "Digest: $($script:Manifest.contentDigest)"
    }
    return $true
}

function Show-Providers {
    $rows = foreach ($provider in $script:Manifest.providers) {
        [pscustomobject]@{
            ID = $provider.id
            Product = $provider.product
            PackagePath = "$($provider.id)\$($provider.nativeSkillRoot)"
            UserInstall = $UserSkillRoots[$provider.id]
            ProjectInstall = $ProjectSkillRoots[$provider.id]
        }
    }
    $rows | Format-Table -AutoSize
}

function Confirm-Action {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Question,
        [bool]$AssumeYes
    )

    if ($AssumeYes) {
        return $true
    }

    $answer = (Read-Host "$Question [y/N]").Trim().ToLowerInvariant()
    return @("y", "yes", "c", "co", "có") -contains $answer
}

function Install-ProviderPack {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProviderId,
        [Parameter(Mandatory = $true)]
        [ValidateSet("user", "project")]
        [string]$Scope,
        [string]$ProjectPath,
        [Parameter(Mandatory = $true)]
        [string]$BackupStamp
    )

    $provider = Get-Provider $ProviderId
    $sourceRoot = Join-Path (Join-Path $PackageRoot $provider.id) $provider.nativeSkillRoot
    $destinationRoot = Get-DestinationRoot $ProviderId $Scope $ProjectPath
    $destinationParent = Split-Path -Parent $destinationRoot
    $backupRoot = Join-Path (Join-Path $destinationParent "bkit-skill-backups") (Join-Path $BackupStamp $ProviderId)

    New-Item -ItemType Directory -Path $destinationRoot -Force | Out-Null
    $installed = 0
    $backedUp = 0

    foreach ($sourceSkill in Get-ChildItem -LiteralPath $sourceRoot -Directory | Sort-Object Name) {
        $destinationSkill = Join-Path $destinationRoot $sourceSkill.Name
        if (Test-Path -LiteralPath $destinationSkill) {
            New-Item -ItemType Directory -Path $backupRoot -Force | Out-Null
            Copy-Item -LiteralPath $destinationSkill -Destination $backupRoot -Recurse -Force
            Remove-Item -LiteralPath $destinationSkill -Recurse -Force
            $backedUp += 1
        }

        Copy-Item -LiteralPath $sourceSkill.FullName -Destination $destinationRoot -Recurse -Force
        $installed += 1

        $sourceContent = Get-Content -Raw -LiteralPath (Join-Path $sourceSkill.FullName "SKILL.md") -Encoding UTF8
        $installedContent = Get-Content -Raw -LiteralPath (Join-Path $destinationSkill "SKILL.md") -Encoding UTF8
        if ($sourceContent -ne $installedContent) {
            throw "Xac minh sau cai dat that bai: $ProviderId/$($sourceSkill.Name)"
        }
    }

    Write-Host "PASS $ProviderId -> $destinationRoot ($installed skills, $backedUp backup)" -ForegroundColor Green
    if ($backedUp -gt 0) {
        Write-Host "  Backup: $backupRoot"
    }
}

function Install-Package {
    param(
        [string]$ProviderName,
        [string]$ScopeName,
        [string]$ProjectPath,
        [bool]$AssumeYes
    )

    Test-Package -Quiet | Out-Null
    $providerIds = @(Resolve-ProviderIds $ProviderName)
    $scope = if ([string]::IsNullOrWhiteSpace($ScopeName)) {
        "user"
    } else {
        $ScopeName.Trim().ToLowerInvariant()
    }

    if (@("user", "project") -notcontains $scope) {
        throw "Scope phai la 'user' hoac 'project'."
    }

    if ($scope -eq "project" -and [string]::IsNullOrWhiteSpace($ProjectPath)) {
        $ProjectPath = Read-Host "Nhap duong dan project dich"
    }

    Write-Host "Se cai cac goi sau:" -ForegroundColor Yellow
    foreach ($providerId in $providerIds) {
        $destination = Get-DestinationRoot $providerId $scope $ProjectPath
        Write-Host "  $providerId -> $destination"
    }
    Write-Host "Skill trung ten se duoc sao luu truoc khi thay the." -ForegroundColor Yellow

    if (-not (Confirm-Action "Tiep tuc cai dat?" $AssumeYes)) {
        Write-Host "Da huy."
        return
    }

    $backupStamp = Get-Date -Format "yyyyMMdd-HHmmss"
    foreach ($providerId in $providerIds) {
        Install-ProviderPack $providerId $scope $ProjectPath $backupStamp
    }
}

function New-ZipPackage {
    param([string]$Version)

    Test-Package -Quiet | Out-Null

    if ([string]::IsNullOrWhiteSpace($Version)) {
        $Version = Get-Date -Format "yyyyMMdd-HHmmss"
    }
    if ($Version -notmatch "^[A-Za-z0-9][A-Za-z0-9._-]*$") {
        throw "Version chi duoc gom chu, so, dau cham, gach duoi va gach ngang."
    }

    $distRoot = Join-Path $PackageRoot "dist"
    New-Item -ItemType Directory -Path $distRoot -Force | Out-Null
    $zipPath = Join-Path $distRoot "bkit-agent-skills-$Version.zip"
    $temporaryZip = Join-Path $distRoot ".bkit-agent-skills-$Version.tmp.zip"
    $stageRoot = Join-Path ([IO.Path]::GetTempPath()) ("bkit-agent-skills-" + [guid]::NewGuid().ToString("N"))

    try {
        New-Item -ItemType Directory -Path $stageRoot -Force | Out-Null
        foreach ($item in Get-ChildItem -LiteralPath $PackageRoot -Force) {
            if ($item.Name -eq "dist") {
                continue
            }
            Copy-Item -LiteralPath $item.FullName -Destination $stageRoot -Recurse -Force
        }

        Add-Type -AssemblyName System.IO.Compression.FileSystem
        if (Test-Path -LiteralPath $temporaryZip) {
            Remove-Item -LiteralPath $temporaryZip -Force
        }
        [IO.Compression.ZipFile]::CreateFromDirectory(
            $stageRoot,
            $temporaryZip,
            [IO.Compression.CompressionLevel]::Optimal,
            $false
        )

        $archive = [IO.Compression.ZipFile]::OpenRead($temporaryZip)
        try {
            $skillCount = @($archive.Entries | Where-Object { $_.FullName -match "(^|[\\/])SKILL\.md$" }).Count
            $hasInstaller = $null -ne ($archive.Entries | Where-Object { $_.FullName -eq "bkit.bat" } | Select-Object -First 1)
            if ($skillCount -ne 400 -or -not $hasInstaller) {
                throw "ZIP khong hop le: SKILL.md=$skillCount, bkit.bat=$hasInstaller"
            }
        } finally {
            $archive.Dispose()
        }

        Move-Item -LiteralPath $temporaryZip -Destination $zipPath -Force
        $hash = (Get-FileHash -LiteralPath $zipPath -Algorithm SHA256).Hash
        Write-Host "PASS: Da tao ZIP." -ForegroundColor Green
        Write-Host "File: $zipPath"
        Write-Host "SHA256: $hash"
        Write-Host "Cach dung: giai nen ZIP, sau do nhap dup bkit.bat hoac chay 'bkit.bat install'."
    } finally {
        if (Test-Path -LiteralPath $stageRoot) {
            Remove-Item -LiteralPath $stageRoot -Recurse -Force
        }
        if (Test-Path -LiteralPath $temporaryZip) {
            Remove-Item -LiteralPath $temporaryZip -Force
        }
    }
}

function Write-Usage {
    Write-Host @"
Su dung:
  bkit.bat
      Mo menu tuong tac.

  bkit.bat list
      Liet ke provider va duong dan cai dat.

  bkit.bat verify
      Kiem tra toan bo 10 goi va 400 SKILL.md.

  bkit.bat pack [version]
      Tao dist\bkit-agent-skills-[version].zip.

  bkit.bat install [provider|all] [user|project] [project-path] [yes]
      Cai 40 skills cho provider, hoac 9 agent khi chon all.

Vi du:
  bkit.bat pack 1.0.0
  bkit.bat install codex user "" yes
  bkit.bat install claude project "C:\source\my-project" yes
  bkit.bat install all user

Provider ngan:
  codex, claude, copilot, gemini, cursor, cline, windsurf,
  kilo, opencode, standard
"@
}

function Invoke-InteractiveMenu {
    Write-Banner
    Write-Host "1. Cai dat skills"
    Write-Host "2. Tao file ZIP"
    Write-Host "3. Kiem tra goi"
    Write-Host "4. Liet ke provider"
    Write-Host "0. Thoat"
    $choice = (Read-Host "Chon").Trim()

    switch ($choice) {
        "1" {
            Show-Providers
            $provider = Read-Host "Provider (mac dinh: all)"
            $scope = Read-Host "Scope user/project (mac dinh: user)"
            Install-Package $provider $scope "" $false
        }
        "2" {
            $version = Read-Host "Version (Enter de dung timestamp)"
            New-ZipPackage $version
        }
        "3" {
            Test-Package | Out-Null
        }
        "4" {
            Show-Providers
        }
        "0" {
            return
        }
        default {
            throw "Lua chon khong hop le."
        }
    }

    if ($choice -ne "0") {
        Write-Host ""
        [void](Read-Host "Nhan Enter de dong")
    }
}

try {
    $script:Manifest = Get-Manifest
    $action = if ([string]::IsNullOrWhiteSpace($env:BKIT_ARG1)) {
        ""
    } else {
        $env:BKIT_ARG1.Trim().ToLowerInvariant()
    }

    switch ($action) {
        "" {
            Invoke-InteractiveMenu
        }
        "list" {
            Write-Banner
            Show-Providers
        }
        "verify" {
            Write-Banner
            Test-Package | Out-Null
        }
        "pack" {
            Write-Banner
            New-ZipPackage $env:BKIT_ARG2
        }
        "install" {
            Write-Banner
            $yesArgument = if ($null -eq $env:BKIT_ARG5) { "" } else { $env:BKIT_ARG5.Trim().ToLowerInvariant() }
            $assumeYes = @("y", "yes", "true", "1") -contains $yesArgument
            Install-Package $env:BKIT_ARG2 $env:BKIT_ARG3 $env:BKIT_ARG4 $assumeYes
        }
        { @("help", "-h", "--help", "/?") -contains $_ } {
            Write-Banner
            Write-Usage
        }
        default {
            throw "Lenh '$action' khong hop le. Chay 'bkit.bat help'."
        }
    }
    exit 0
} catch {
    Write-Host ""
    Write-Host "LOI: $($_.Exception.Message)" -ForegroundColor Red
    if ([string]::IsNullOrWhiteSpace($env:BKIT_ARG1)) {
        [void](Read-Host "Nhan Enter de dong")
    }
    exit 1
}
