# =============================================================================
# setup-sharepoint.ps1
#
# CredManager 導入用の「検出駆動」セットアップスクリプト。
#   1. サインイン後、テナント ID と SharePoint ルートを検出 (取れない/未提示の値は対話入力)
#   2. クライアント ID は Entra ID 上で表示名 "CredManager" のアプリ登録を検索 → 採用、
#      無ければ管理者に入力を求める
#   3. SharePoint サイト名は既定 "<root>/sites/CredManager" を提示して確認
#   4. Key Vault URI は管理者に入力を求める (Key Vault は自動作成しない)
#   5. SharePoint サイト作成 (未存在のとき) + リスト/列作成 (冪等、IsDeleted 含む)
#   6. environment.json (固定スキーマ 5 項目) を生成
#
# 本スクリプトは「導入者の PC」で実行するためのものです。各利用者の PC を直接
# 構成するスクリプトではありません。生成した environment.json を CredManager の
# exe と同じフォルダに同梱して配布することで、利用者は環境設定を手入力する
# 必要なくアプリを使い始められます。
#
# 起動方法:
#   - アプリの「環境設定 (管理者向け)」画面の「SP サイト作成」ボタンから起動可能。
#     その場合 -EnvironmentJsonOutputPath が自動で渡され、他の値は対話入力で解決。
#   - 直接実行する場合は param ブロックの引数で値を渡せばプロンプトをスキップできる。
#
# 前提:
#   - PnP.PowerShell モジュール (v2 以降) がインストールされていること:
#       Install-Module PnP.PowerShell -Scope CurrentUser
#   - 操作者は MS365 アカウントで対話的にサインインします (-Interactive)。
#   - サイト作成にはテナントのサイト作成権限 (SharePoint 管理者等) が必要です。
#     権限不足で作成に失敗した場合、その旨を明示して終了します。
# =============================================================================

param(
    # 任意。指定しなければサインイン後に Graph /organization から自動検出する。
    [string]$TenantId,

    # 任意。指定しなければ「ルート URL or テナント名」を 1 度プロンプトする。
    # "https://contoso.sharepoint.com" でも "contoso" でも受け付ける。
    [string]$SharePointRootUrl,

    # 任意。指定しなければ既定 "<root>/sites/CredManager" を提示して確認入力。
    [string]$SiteUrl,

    # 任意。サイト作成時の表示名。
    [string]$SiteTitle = "CredManager",

    # 固定既定 (アプリの environment.json と一致させる)。
    [string]$ListName = "CredManagerItems",

    # 任意。指定しなければ Graph で表示名 "CredManager" を検索 → 1 件採用、無ければ入力。
    [string]$ClientId,

    # 任意。指定しなければプロンプト。Key Vault の自動作成はしない。
    [string]$KeyVaultUri,

    # 任意。指定しなければスクリプトと同じフォルダの environment.json に書き出す。
    [string]$EnvironmentJsonOutputPath,

    # 任意。指定しなければスクリプトと同階層の accessTiers.json を探す。
    # ウィザードからの起動では、setup-access-tiers.ps1 が temp に書いたパスをここに渡す。
    [string]$AccessTiersJsonPath,

    # 非対話モード (ウィザード等から呼ばれるとき)。Read-Host "Enter で終了します" をスキップする。
    [switch]$NonInteractive
)

function Pause { if (-not $NonInteractive) { Read-Host "Enter で終了します" } }

# ===== 既定値 =====
# PnP との Connect で当初使う ClientId。CredManager と同じアプリ ID を流用。
# このアプリが当該テナントに登録済みであることが前提 (未登録なら -ClientId で別の
# 多要件対応アプリ ID を渡すか、先にアプリ登録を行うこと)。
$DefaultClientId = "eef1c3d3-3c89-442b-84f9-a3e48660a2de"

# ===== モジュール確認・自動インストール =====
# PnP.PowerShell の現行版 (2.x / 3.x) は PS 7+ 専用。Windows PowerShell 5.1 から
# 起動された場合は 1.12.0 (5.1 対応最終版) を入れる。既にロード可能なら何もしない。

$IsPS7Plus = $PSVersionTable.PSVersion.Major -ge 7

function Test-PnPLoadable {
    param([string]$RequiredVersion)
    try {
        if ($RequiredVersion) {
            Import-Module PnP.PowerShell -RequiredVersion $RequiredVersion -ErrorAction Stop
        } else {
            Import-Module PnP.PowerShell -ErrorAction Stop
        }
        return $true
    } catch {
        return $false
    }
}

function Ensure-NuGetProvider {
    if (Get-PackageProvider -Name NuGet -ErrorAction SilentlyContinue) { return $true }
    Write-Host "  NuGet パッケージプロバイダをインストールしています..." -ForegroundColor Cyan
    try {
        Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser -ErrorAction Stop | Out-Null
        return $true
    } catch {
        Write-Host "  NuGet プロバイダの導入に失敗: $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

function Install-PnPForCurrentEdition {
    # TLS 1.2 を確実に有効化 (古い Windows で必要)
    try {
        [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
    } catch {
        # 古い .NET ランタイムで失敗しても続行 (PSGallery 接続時に再度失敗するなら下で検出される)
    }

    if (-not (Ensure-NuGetProvider)) { return $false }

    # PSGallery を信頼済みに (確認プロンプト抑制)
    try {
        $repo = Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue
        if ($repo -and $repo.InstallationPolicy -ne "Trusted") {
            Set-PSRepository -Name PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
        }
    } catch {
        # -Force で抑制するので無視
    }

    try {
        if ($IsPS7Plus) {
            Write-Host "  PowerShell 7+ を検出。最新の PnP.PowerShell をインストールします..." -ForegroundColor Cyan
            Install-Module PnP.PowerShell -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
        } else {
            Write-Host "  Windows PowerShell 5.1 を検出。PnP.PowerShell 1.12.0 (5.1 対応最終版) をインストールします..." -ForegroundColor Cyan
            Install-Module PnP.PowerShell -RequiredVersion 1.12.0 -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
        }
        return $true
    } catch {
        Write-Host "  PnP.PowerShell のインストールに失敗しました: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host "  考えられる原因:" -ForegroundColor Yellow
        Write-Host "    - インターネット非接続 / 企業プロキシで PSGallery がブロック" -ForegroundColor Yellow
        Write-Host "    - PowerShell の実行ポリシーや TLS 設定" -ForegroundColor Yellow
        Write-Host "  手動インストール:" -ForegroundColor Yellow
        if ($IsPS7Plus) {
            Write-Host "    pwsh: Install-Module PnP.PowerShell -Scope CurrentUser -Force" -ForegroundColor Yellow
        } else {
            Write-Host "    powershell.exe: Install-Module PnP.PowerShell -RequiredVersion 1.12.0 -Scope CurrentUser -Force" -ForegroundColor Yellow
        }
        return $false
    }
}

Write-Host "PnP.PowerShell モジュールを確認しています..." -ForegroundColor Cyan
$pnpReady = $false
$preferredVersion = if ($IsPS7Plus) { $null } else { "1.12.0" }

# まず既存モジュールでロードできるか試す
if (Test-PnPLoadable -RequiredVersion $preferredVersion) {
    $pnpReady = $true
    Write-Host "  PnP.PowerShell をロードしました (バージョン: $((Get-Module PnP.PowerShell).Version))" -ForegroundColor Green
}

# ロードできない場合は現在のエディションで動くバージョンを追加インストール
if (-not $pnpReady) {
    Write-Host "  PnP.PowerShell が現在のエディションでロードできません。インストールを試みます。" -ForegroundColor Yellow
    if (Install-PnPForCurrentEdition) {
        if (Test-PnPLoadable -RequiredVersion $preferredVersion) {
            $pnpReady = $true
            Write-Host "  PnP.PowerShell をロードしました (バージョン: $((Get-Module PnP.PowerShell).Version))" -ForegroundColor Green
        }
    }
}

if (-not $pnpReady) {
    Write-Host "PnP.PowerShell を準備できなかったため終了します。" -ForegroundColor Red
    Pause
    exit 1
}

# ===== ① サインイン用の URL を決定 (最小入力) =====
# NOTE: param 名に $input は使えない (PowerShell の自動変数と衝突して空扱いになる)。
function Normalize-RootUrl {
    param([string]$RawUrl)
    if ([string]::IsNullOrWhiteSpace($RawUrl)) { return $null }
    $t = $RawUrl.Trim().TrimEnd('/')
    if ($t -notmatch '^https?://') {
        # テナント名 (contoso) と見なす
        $t = "https://$t.sharepoint.com"
    }
    return $t
}

$connectUrl = $null
if ($SharePointRootUrl) {
    $connectUrl = Normalize-RootUrl $SharePointRootUrl
} elseif ($SiteUrl) {
    try {
        $u = [Uri]$SiteUrl
        $connectUrl = "$($u.Scheme)://$($u.Host)"
    } catch { $connectUrl = $null }
}

if (-not $connectUrl) {
    Write-Host ""
    Write-Host "サインインに必要な情報を 1 つだけ教えてください。" -ForegroundColor Cyan
    Write-Host "  例: 'https://contoso.sharepoint.com' または 'contoso'" -ForegroundColor DarkGray
    $userInput = Read-Host "SharePoint のルート URL またはテナント名"
    $connectUrl = Normalize-RootUrl $userInput
    if (-not $connectUrl) {
        Write-Host "ルート URL が空のため終了します。" -ForegroundColor Red
        Pause
        exit 1
    }
}

# 既存 -ClientId 引数が空のときは PnP Connect で既定 ClientId を使う
$connectClientId = if ($ClientId) { $ClientId } else { $DefaultClientId }

# ===== ② 対話的サインイン =====
Write-Host ""
Write-Host "サインインします (別ウィンドウのブラウザが開きます)..." -ForegroundColor Cyan
Write-Host "  Connect URL: $connectUrl" -ForegroundColor DarkGray
Write-Host "  Client ID  : $connectClientId" -ForegroundColor DarkGray
try {
    Connect-PnPOnline -Url $connectUrl -Interactive -ClientId $connectClientId -ErrorAction Stop
} catch {
    Write-Host "サインインに失敗しました: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "考えられる原因:" -ForegroundColor Yellow
    Write-Host "  - 入力した URL/テナント名が誤っている" -ForegroundColor Yellow
    Write-Host "  - 当該テナントに Entra アプリ ($connectClientId) が登録されていない" -ForegroundColor Yellow
    Write-Host "    → 「アプリ登録」画面 (Entra 管理センター) で CredManager 用アプリを登録し、" -ForegroundColor Yellow
    Write-Host "       -ClientId <AppId> パラメータで再実行してください。" -ForegroundColor Yellow
    Pause
    exit 1
}

# ===== ③ テナント ID を自動検出 (Graph /organization 経由) =====
if (-not $TenantId) {
    Write-Host ""
    Write-Host "テナント ID を自動検出しています..." -ForegroundColor Cyan
    try {
        $org = Invoke-PnPGraphMethod -Url "organization?`$select=id" -ErrorAction Stop
        if ($org -and $org.value -and $org.value.Count -ge 1) {
            $TenantId = ($org.value[0].id).ToString()
            Write-Host "  検出しました: $TenantId" -ForegroundColor Green
        }
    } catch {
        Write-Host "  自動検出に失敗しました: $($_.Exception.Message)" -ForegroundColor Yellow
    }
    if (-not $TenantId) {
        $TenantId = Read-Host "テナント ID (GUID) を入力してください"
        if ([string]::IsNullOrWhiteSpace($TenantId)) {
            Write-Host "テナント ID が空のため終了します。" -ForegroundColor Red
            Pause
            exit 1
        }
    }
}

# SharePoint ルート URL は、接続に成功した URL をそのまま採用
$SharePointRootUrl = $connectUrl

# ===== ④ サイト URL の既定提示・確認 =====
if (-not $SiteUrl) {
    $defaultSiteUrl = "$SharePointRootUrl/sites/CredManager"
    $entered = Read-Host "CredManager のサイト URL [既定: $defaultSiteUrl]"
    if ([string]::IsNullOrWhiteSpace($entered)) { $SiteUrl = $defaultSiteUrl } else { $SiteUrl = $entered.Trim() }
}
$SiteUrl = $SiteUrl.TrimEnd('/')

# ===== ⑤ ClientId を Graph で表示名検索 → 採用 or 入力 =====
if (-not $ClientId) {
    Write-Host ""
    Write-Host "Entra ID で表示名 'CredManager' のアプリ登録を検索しています..." -ForegroundColor Cyan
    $foundAppId = $null
    try {
        $apps = Invoke-PnPGraphMethod -Url "applications?`$filter=displayName eq 'CredManager'&`$select=id,appId,displayName" -ErrorAction Stop
        if ($apps -and $apps.value) {
            if ($apps.value.Count -eq 1) {
                $foundAppId = $apps.value[0].appId
                Write-Host "  見つかりました: $foundAppId" -ForegroundColor Green
            } elseif ($apps.value.Count -gt 1) {
                Write-Host "  複数のアプリが見つかりました。手入力が必要です。" -ForegroundColor Yellow
            } else {
                Write-Host "  見つかりませんでした。" -ForegroundColor Yellow
            }
        }
    } catch {
        Write-Host "  Graph 検索に失敗しました (権限不足の可能性あり): $($_.Exception.Message)" -ForegroundColor Yellow
    }
    if ($foundAppId) {
        $ClientId = $foundAppId
    } else {
        Write-Host "  「アプリ登録」画面で CredManager 用のアプリ登録を作成し、その AppId を入力してください。" -ForegroundColor Yellow
        $ClientId = Read-Host "クライアント ID (Entra アプリの AppId)"
        if ([string]::IsNullOrWhiteSpace($ClientId)) {
            Write-Host "クライアント ID が空のため終了します。" -ForegroundColor Red
            Pause
            exit 1
        }
    }
}

# ===== ⑥ Key Vault URI のプロンプト =====
if (-not $KeyVaultUri) {
    Write-Host ""
    $KeyVaultUri = Read-Host "Azure Key Vault URI (例: https://<YOUR_VAULT_NAME>.vault.azure.net/)"
    if ([string]::IsNullOrWhiteSpace($KeyVaultUri)) {
        Write-Host "Key Vault URI が空のため終了します。" -ForegroundColor Red
        Pause
        exit 1
    }
}

# ===== ⑦ environment.json 出力先 =====
# 既定では生成しない (マルチテナント・ゼロ設定運用)。
# -EnvironmentJsonOutputPath が明示されたときのみ後段で書き出す。
# accessTiers.json 探索用のスクリプト同階層パスは確保しておく。
if ($PSScriptRoot) { $defaultEnvDir = $PSScriptRoot } else { $defaultEnvDir = (Get-Location).Path }

# ===== 解決結果サマリ =====
Write-Host ""
Write-Host "解決した値:" -ForegroundColor Cyan
Write-Host "  TenantId           : $TenantId"
Write-Host "  ClientId           : $ClientId"
Write-Host "  SharePointSiteUrl  : $SiteUrl"
Write-Host "  SharePointListName : $ListName"
Write-Host "  KeyVaultUri        : $KeyVaultUri"
Write-Host "  EnvironmentJsonOut : $EnvironmentJsonOutputPath"

# =============================================================================
# サイト作成 / リスト・列作成 / environment.json 生成
# =============================================================================

# -----------------------------------------------------------------------------
# サイトの存在確認 (まず対象サイトへ直接接続を試みる)
# -----------------------------------------------------------------------------
Write-Host ""
Write-Host "サイトの存在を確認しています: $SiteUrl" -ForegroundColor Cyan
$siteExists = $false
try {
    Connect-PnPOnline -Url $SiteUrl -Interactive -ClientId $connectClientId -ErrorAction Stop
    Get-PnPWeb -ErrorAction Stop | Out-Null
    $siteExists = $true
    Write-Host "  サイトは既に存在します。そのまま使用します。" -ForegroundColor Green
} catch {
    Write-Host "  サイトが見つかりませんでした (新規作成します): $($_.Exception.Message)" -ForegroundColor Yellow
}

# -----------------------------------------------------------------------------
# サイト作成 (Communication Site)
# -----------------------------------------------------------------------------
if (-not $siteExists) {
    Write-Host ""
    Write-Host "テナントへ接続してサイトを作成します: $SharePointRootUrl" -ForegroundColor Cyan
    try {
        Connect-PnPOnline -Url $SharePointRootUrl -Interactive -ClientId $connectClientId -ErrorAction Stop
    } catch {
        Write-Host "テナントへの接続に失敗しました: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host "サイト作成権限 (SharePoint 管理者など) を持つアカウントでサインインしてください。" -ForegroundColor Yellow
        Pause
        exit 1
    }

    Write-Host "コミュニケーションサイトを作成しています..." -ForegroundColor Cyan
    Write-Host "  Title : $SiteTitle"
    Write-Host "  Url   : $SiteUrl"
    try {
        New-PnPSite -Type CommunicationSite -Title $SiteTitle -Url $SiteUrl -Wait -ErrorAction Stop | Out-Null
        Write-Host "  作成リクエストが完了しました。" -ForegroundColor Green
    } catch {
        Write-Host "サイト作成に失敗しました: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host "考えられる原因:" -ForegroundColor Yellow
        Write-Host "  - サインインしたアカウントにサイト作成権限がない (SharePoint 管理者ロール等が必要)" -ForegroundColor Yellow
        Write-Host "  - 同名 URL のサイトが過去に削除済みリサイクル状態で残っている" -ForegroundColor Yellow
        Write-Host "  - テナントのサイト作成設定で制限がかかっている" -ForegroundColor Yellow
        Pause
        exit 1
    }

    # 反映の時間差を考慮して、新サイトへの接続が成立するまで短いリトライを行う
    Write-Host "サイトへの接続を待機しています..." -ForegroundColor Cyan
    $maxAttempts = 12
    $waitSeconds = 10
    $connected = $false
    for ($i = 1; $i -le $maxAttempts; $i++) {
        Start-Sleep -Seconds $waitSeconds
        try {
            Connect-PnPOnline -Url $SiteUrl -Interactive -ClientId $connectClientId -ErrorAction Stop
            Get-PnPWeb -ErrorAction Stop | Out-Null
            $connected = $true
            Write-Host "  接続成功 ($i 回目)" -ForegroundColor Green
            break
        } catch {
            Write-Host "  待機中 ($i / $maxAttempts) ... $($_.Exception.Message)" -ForegroundColor DarkGray
        }
    }
    if (-not $connected) {
        Write-Host "サイト作成後の接続でタイムアウトしました。少し時間を置いて再実行してください。" -ForegroundColor Red
        Pause
        exit 1
    }
}

# 接続検証
try {
    $ctx = Get-PnPContext -ErrorAction Stop
    if (-not $ctx) { throw "PnP コンテキストが取得できませんでした。" }
    Write-Host "サイトへの接続を確認しました。" -ForegroundColor Green
} catch {
    Write-Host "PnP 接続検証に失敗しました: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "サインインがキャンセル/失敗した可能性があります。再度実行してください。" -ForegroundColor Yellow
    Pause
    exit 1
}

# -----------------------------------------------------------------------------
# リスト・列の作成 (冪等)
# -----------------------------------------------------------------------------
Write-Host ""
Write-Host "リスト '$ListName' を確認・作成しています..." -ForegroundColor Cyan
$existing = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue
if ($existing) {
    Write-Host "  リスト '$ListName' は既に存在します。列のみ追加・補完します。" -ForegroundColor Yellow
} else {
    Write-Host "  リスト '$ListName' を作成しています..." -ForegroundColor Cyan
    New-PnPList -Title $ListName -Template GenericList -OnQuickLaunch | Out-Null
}

function Add-OrEnsureField {
    param(
        [string]$InternalName,
        [string]$DisplayName,
        [ValidateSet("Text","Note")] [string]$Type
    )
    $existingField = Get-PnPField -List $ListName -Identity $InternalName -ErrorAction SilentlyContinue
    if ($existingField) {
        Write-Host "  列 $InternalName は既に存在します (skip)" -ForegroundColor DarkGray
        return
    }
    Write-Host "  列 $InternalName を追加: $Type" -ForegroundColor Green
    Add-PnPField -List $ListName -InternalName $InternalName -DisplayName $InternalName -Type $Type -AddToDefaultView | Out-Null
    Set-PnPField -List $ListName -Identity $InternalName -Values @{ Title = $DisplayName } | Out-Null
}

function Add-OrEnsureBooleanField {
    param(
        [string]$InternalName,
        [string]$DisplayName,
        [bool]$DefaultValueTrue = $false
    )
    $existingField = Get-PnPField -List $ListName -Identity $InternalName -ErrorAction SilentlyContinue
    if ($existingField) {
        Write-Host "  列 $InternalName は既に存在します (skip)" -ForegroundColor DarkGray
        return
    }
    Write-Host "  列 $InternalName を追加: Boolean" -ForegroundColor Green
    Add-PnPField -List $ListName -InternalName $InternalName -DisplayName $InternalName -Type Boolean -AddToDefaultView | Out-Null
    $defaultStr = if ($DefaultValueTrue) { "1" } else { "0" }
    Set-PnPField -List $ListName -Identity $InternalName -Values @{ Title = $DisplayName; DefaultValue = $defaultStr } | Out-Null
}

function Add-OrEnsureChoiceField {
    param(
        [string]$InternalName,
        [string]$DisplayName,
        [string[]]$Choices,
        [string]$DefaultValue = "",
        [bool]$Required = $false,
        [bool]$FillInChoice = $false
    )
    $existingField = Get-PnPField -List $ListName -Identity $InternalName -ErrorAction SilentlyContinue
    if ($existingField) {
        Write-Host "  列 $InternalName は既に存在します (skip)" -ForegroundColor DarkGray
        return
    }
    Write-Host "  列 $InternalName を追加: Choice ($($Choices -join ' / '))" -ForegroundColor Green
    Add-PnPField -List $ListName -InternalName $InternalName -DisplayName $InternalName -Type Choice -Choices $Choices -AddToDefaultView | Out-Null

    $values = @{ Title = $DisplayName }
    if ($DefaultValue) { $values["DefaultValue"]  = $DefaultValue }
    if ($Required)     { $values["Required"]      = $true }
    if ($FillInChoice) { $values["FillInChoice"]  = $true }
    Set-PnPField -List $ListName -Identity $InternalName -Values $values | Out-Null
}

# Title 列は既定で存在するので表示名のみ更新
Write-Host "既定の Title 列の表示名を '名前' に設定します" -ForegroundColor Cyan
Set-PnPField -List $ListName -Identity "Title" -Values @{ Title = "名前" } | Out-Null

# --- 初日MVP / フェーズ1 / 1.5 の列 ---
Add-OrEnsureField -InternalName "Description"        -DisplayName "説明"             -Type Note
Add-OrEnsureField -InternalName "ConnectionInfo"     -DisplayName "接続先"           -Type Text
Add-OrEnsureField -InternalName "AccountId"          -DisplayName "アカウントID"     -Type Text
Add-OrEnsureField -InternalName "EncryptedPassword"  -DisplayName "暗号化パスワード" -Type Note
Add-OrEnsureField -InternalName "Iv"                 -DisplayName "初期化ベクトル"   -Type Text
Add-OrEnsureField -InternalName "WrappedKey"         -DisplayName "ラップ済みキー"   -Type Note
Add-OrEnsureField -InternalName "MfaConfigs"         -DisplayName "MFA設定"          -Type Note

# --- フェーズ2 ---
Add-OrEnsureField -InternalName "GroupName"          -DisplayName "グループ"         -Type Text

# --- フェーズ3 ---
Add-OrEnsureChoiceField -InternalName "AccessTier" -DisplayName "アクセス区分" `
    -Choices @("社外秘", "管理部", "4等級以上") `
    -DefaultValue "社外秘" -Required $true -FillInChoice $true

# --- フェーズ4 ---
Add-OrEnsureField -InternalName "AuditLog"           -DisplayName "監査ログ"         -Type Note
Add-OrEnsureBooleanField -InternalName "IsDeleted"   -DisplayName "削除済み"         -DefaultValueTrue $false

Write-Host ""
Write-Host "リスト/列の作成・確認が完了しました。" -ForegroundColor Green
Write-Host "  $SiteUrl/Lists/$ListName" -ForegroundColor Green

# -----------------------------------------------------------------------------
# CredManagerItems のバージョン管理を有効化 (誤削除/誤編集からの item 単位復旧)
# 既に有効なら冪等に何もしない。
# -----------------------------------------------------------------------------
Write-Host ""
Write-Host "リスト '$ListName' のバージョン管理を確認しています..." -ForegroundColor Cyan
try {
    $listInfo = Get-PnPList -Identity $ListName -Includes "EnableVersioning","MajorVersionLimit" -ErrorAction Stop
    if ($listInfo.EnableVersioning) {
        Write-Host "  バージョン管理は既に有効です (MajorVersionLimit=$($listInfo.MajorVersionLimit))" -ForegroundColor DarkGray
    } else {
        Write-Host "  バージョン管理を有効化します..." -ForegroundColor Green
        Set-PnPList -Identity $ListName -EnableVersioning $true -ErrorAction Stop | Out-Null
        Write-Host "  有効化しました。誤削除/誤編集からのリスト item 単位の復旧手段になります。" -ForegroundColor Green
    }
} catch {
    Write-Host "  バージョン管理の確認/有効化に失敗: $($_.Exception.Message)" -ForegroundColor Yellow
    Write-Host "    続行します (リスト機能としては保全されているため致命ではありません)。" -ForegroundColor DarkGray
}

# -----------------------------------------------------------------------------
# CredManagerSetting リスト (Setting 行・AccessTier 行を格納するサイト側設定置き場)
# -----------------------------------------------------------------------------
$SettingListName = "CredManagerSetting"

Write-Host ""
Write-Host "リスト '$SettingListName' を確認・作成しています..." -ForegroundColor Cyan
$settingList = Get-PnPList -Identity $SettingListName -ErrorAction SilentlyContinue
if ($settingList) {
    Write-Host "  リスト '$SettingListName' は既に存在します。列のみ追加・補完します。" -ForegroundColor Yellow
} else {
    Write-Host "  リスト '$SettingListName' を作成しています..." -ForegroundColor Cyan
    New-PnPList -Title $SettingListName -Template GenericList -OnQuickLaunch | Out-Null
}

function Add-OrEnsureFieldForList {
    param(
        [string]$List,
        [string]$InternalName,
        [string]$DisplayName,
        [ValidateSet("Text","Note")] [string]$Type
    )
    $f = Get-PnPField -List $List -Identity $InternalName -ErrorAction SilentlyContinue
    if ($f) { Write-Host "  列 $InternalName は既に存在します (skip)" -ForegroundColor DarkGray; return }
    Write-Host "  列 $InternalName を追加: $Type" -ForegroundColor Green
    Add-PnPField -List $List -InternalName $InternalName -DisplayName $InternalName -Type $Type -AddToDefaultView | Out-Null
    Set-PnPField -List $List -Identity $InternalName -Values @{ Title = $DisplayName } | Out-Null
}

function Add-OrEnsureChoiceFieldForList {
    param(
        [string]$List,
        [string]$InternalName,
        [string]$DisplayName,
        [string[]]$Choices,
        [bool]$Required = $false
    )
    $f = Get-PnPField -List $List -Identity $InternalName -ErrorAction SilentlyContinue
    if ($f) { Write-Host "  列 $InternalName は既に存在します (skip)" -ForegroundColor DarkGray; return }
    Write-Host "  列 $InternalName を追加: Choice" -ForegroundColor Green
    Add-PnPField -List $List -InternalName $InternalName -DisplayName $InternalName -Type Choice -Choices $Choices -AddToDefaultView | Out-Null
    $values = @{ Title = $DisplayName }
    if ($Required) { $values["Required"] = $true }
    Set-PnPField -List $List -Identity $InternalName -Values $values | Out-Null
}

# Title 列の表示名 (Setting 行はキー、AccessTier 行は区分名として使う)
Set-PnPField -List $SettingListName -Identity "Title" -Values @{ Title = "キー / 区分名" } | Out-Null

Add-OrEnsureChoiceFieldForList -List $SettingListName -InternalName "EntryType" -DisplayName "種別" `
    -Choices @("Setting","AccessTier") -Required $true
Add-OrEnsureFieldForList -List $SettingListName -InternalName "SettingValue"    -DisplayName "値"               -Type Note
Add-OrEnsureFieldForList -List $SettingListName -InternalName "KeyVaultKeyName" -DisplayName "Key Vault キー名" -Type Text
Add-OrEnsureFieldForList -List $SettingListName -InternalName "EntraGroupId"    -DisplayName "Entra グループ ID" -Type Text

# === 行の投入 (Title × EntryType をキーに、既存は更新・無ければ追加) ===
function Set-OrAddCredManagerSettingItem {
    param(
        [string]$Title,
        [string]$EntryType,
        [hashtable]$ExtraValues
    )
    $existing = Get-PnPListItem -List $SettingListName -PageSize 500 |
        Where-Object { $_.FieldValues.Title -eq $Title -and $_.FieldValues.EntryType -eq $EntryType } |
        Select-Object -First 1
    $values = @{ Title = $Title; EntryType = $EntryType }
    foreach ($k in $ExtraValues.Keys) { $values[$k] = $ExtraValues[$k] }
    if ($existing) {
        Write-Host "  $EntryType '$Title' を更新" -ForegroundColor DarkGray
        Set-PnPListItem -List $SettingListName -Identity $existing.Id -Values $values | Out-Null
    } else {
        Write-Host "  $EntryType '$Title' を追加" -ForegroundColor Green
        Add-PnPListItem -List $SettingListName -Values $values | Out-Null
    }
}

Write-Host ""
Write-Host "CredManagerSetting の行を投入しています..." -ForegroundColor Cyan
Set-OrAddCredManagerSettingItem -Title "KeyVaultUri" -EntryType "Setting" -ExtraValues @{ SettingValue = $KeyVaultUri }

# --- AccessTier 行: accessTiers.json があれば内容を、無ければ対話入力 ---
# 解決順: -AccessTiersJsonPath 引数 → スクリプト同階層の accessTiers.json
$tiersData = @()
$nativeKeyBackupUtc = $null
if ($AccessTiersJsonPath -and (Test-Path $AccessTiersJsonPath)) {
    $accessTiersPath = $AccessTiersJsonPath
} else {
    $accessTiersPath = Join-Path $defaultEnvDir "accessTiers.json"
}
if (Test-Path $accessTiersPath) {
    try {
        $cfg = Get-Content -Raw $accessTiersPath | ConvertFrom-Json
        if ($cfg.AccessTiers) {
            foreach ($t in $cfg.AccessTiers) {
                if ($t.DisplayName) {
                    $tiersData += [pscustomobject]@{
                        DisplayName        = $t.DisplayName
                        KeyVaultKeyName    = $t.KeyVaultKeyName
                        EntraGroupObjectId = $t.EntraGroupObjectId
                    }
                }
            }
            Write-Host "  accessTiers.json から $($tiersData.Count) 区分を読み込みました。" -ForegroundColor DarkGray
        }
        # NativeKeyBackupUtc は setup-access-tiers.ps1 が記録した最新のネイティブバックアップ日時。
        # 「Key Vault バックアップ状態」画面 (EnvSettings) で参照する。
        if ($cfg.PSObject.Properties.Match("NativeKeyBackupUtc").Count -gt 0 -and $cfg.NativeKeyBackupUtc) {
            $nativeKeyBackupUtc = "$($cfg.NativeKeyBackupUtc)"
            Write-Host "  NativeKeyBackupUtc を取り込みました: $nativeKeyBackupUtc" -ForegroundColor DarkGray
        }
    } catch {
        Write-Host "  accessTiers.json の読み込みに失敗: $($_.Exception.Message)" -ForegroundColor Yellow
    }
}
if ($tiersData.Count -eq 0) {
    Write-Host ""
    Write-Host "accessTiers.json が見つからないため、区分対応を対話入力します (区分名を空 Enter で終了)" -ForegroundColor Cyan
    while ($true) {
        $name = Read-Host "区分の表示名"
        if ([string]::IsNullOrWhiteSpace($name)) { break }
        $keyName = Read-Host "  $name の Key Vault キー名"
        $groupId = Read-Host "  $name の Entra グループ Object ID"
        $tiersData += [pscustomobject]@{
            DisplayName        = $name
            KeyVaultKeyName    = $keyName
            EntraGroupObjectId = $groupId
        }
    }
}
foreach ($tier in $tiersData) {
    Set-OrAddCredManagerSettingItem -Title $tier.DisplayName -EntryType "AccessTier" -ExtraValues @{
        KeyVaultKeyName = $tier.KeyVaultKeyName
        EntraGroupId    = $tier.EntraGroupObjectId
    }
}

# NativeKeyBackupUtc を Setting 行として upsert (accessTiers.json から取り込めていれば)
if ($nativeKeyBackupUtc) {
    Set-OrAddCredManagerSettingItem -Title "NativeKeyBackupUtc" -EntryType "Setting" -ExtraValues @{
        SettingValue = $nativeKeyBackupUtc
    }
}

Write-Host ""
Write-Host "CredManagerSetting の投入が完了しました。" -ForegroundColor Green
Write-Host "  $SiteUrl/Lists/$SettingListName" -ForegroundColor Green

# -----------------------------------------------------------------------------
# environment.json 生成 (任意。-EnvironmentJsonOutputPath を明示したときのみ)
# マルチテナント・ゼロ設定運用では environment.json を配布しない。
# シングルテナント上書き運用 (環境固有値を事前配布する場合) で必要なときだけ生成する。
# -----------------------------------------------------------------------------
if ($EnvironmentJsonOutputPath) {
    Write-Host ""
    Write-Host "environment.json を生成しています (明示パス指定モード)..." -ForegroundColor Cyan
    $envObject = [ordered]@{
        TenantId           = $TenantId
        ClientId           = $ClientId
        SharePointSiteUrl  = $SiteUrl
        SharePointListName = $ListName
        KeyVaultUri        = $KeyVaultUri
    }
    try {
        $envJson = $envObject | ConvertTo-Json -Depth 5
        $resolvedEnvPath = [System.IO.Path]::GetFullPath($EnvironmentJsonOutputPath)
        $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
        [System.IO.File]::WriteAllText($resolvedEnvPath, $envJson, $utf8NoBom)
        Write-Host "  生成しました: $resolvedEnvPath" -ForegroundColor Green
    } catch {
        Write-Host "environment.json の生成に失敗しました: $($_.Exception.Message)" -ForegroundColor Red
        Disconnect-PnPOnline
        Pause
        exit 1
    }
} else {
    Write-Host ""
    Write-Host "environment.json は生成していません (マルチテナント・ゼロ設定運用)。" -ForegroundColor DarkGray
    Write-Host "  シングルテナント上書きで生成したい場合は -EnvironmentJsonOutputPath <path> を指定して再実行してください。" -ForegroundColor DarkGray
}

Write-Host ""
Write-Host "完了しました。次のステップ:" -ForegroundColor Green
Write-Host "  1. 利用者 PC に CredManager.exe を配置してください (environment.json は不要。サイトから自動取得します)。" -ForegroundColor Green
Write-Host "  2. 利用者を Entra ID のアクセスグループに追加してください。" -ForegroundColor Green
Write-Host "  3. Key Vault キーへの RBAC (Key Vault Crypto User) を該当グループに付与してください。" -ForegroundColor Green
Write-Host ""

Disconnect-PnPOnline
Pause
