<?php

declare(strict_types=1);

require dirname(__DIR__) . '/app/bootstrap.php';

use App\Core\Database;
use App\Services\DiarWorkspaceNavigation;

$apply = in_array('--apply', $argv, true);
$branch = 'njch';
$actor = 'DIAR6B5_R27_ROLE_TEMPLATE';

function roleOutput(array $payload, int $code = 0): void
{
    echo json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit($code);
}

/** @return array<string,mixed> */
function roleReadiness(string $branch): array
{
    $objects = Database::fetchOne($branch, "SELECT COUNT(*) object_count
        FROM USER_TABLES
        WHERE TABLE_NAME IN (
            'DASH_ACL_ROLES',
            'DASH_ACL_USER_ROLES',
            'DASH_ACL_ROLE_OVERRIDES',
            'DASH_ACL_AUDIT'
        )");
    $columns = Database::fetchOne($branch, "SELECT COUNT(*) column_count
        FROM USER_TAB_COLUMNS
        WHERE TABLE_NAME='DASH_ACL_ROLES'
          AND COLUMN_NAME IN (
            'ROLE_DESCRIPTION_EN','ROLE_DESCRIPTION_AR','POLICY_MODE',
            'AUTO_INCLUDE_NEW','ALL_BRANCHES','IS_SYSTEM_ROLE',
            'IS_PROTECTED','IS_ACTIVE','UPDATED_BY','UPDATED_AT'
          )");
    $sequence = Database::fetchOne($branch, "SELECT COUNT(*) sequence_count
        FROM USER_SEQUENCES WHERE SEQUENCE_NAME='DASH_ACL_AUDIT_SEQ'");

    return [
        'tables' => (int)($objects['object_count'] ?? 0),
        'columns' => (int)($columns['column_count'] ?? 0),
        'audit_sequence' => (int)($sequence['sequence_count'] ?? 0),
    ];
}

try {
    $readiness = roleReadiness($branch);
    $permissions = (array)\config('access_control.permissions', []);
    $templates = DiarWorkspaceNavigation::roleTemplates();
    $missingPermissions = [];

    foreach ($templates as $template) {
        foreach ((array)($template['permissions'] ?? []) as $permission) {
            if (!isset($permissions[(string)$permission])) {
                $missingPermissions[(string)$permission] = true;
            }
        }
    }

    $ready = $readiness['tables'] === 4
        && $readiness['columns'] === 10
        && $readiness['audit_sequence'] === 1
        && $missingPermissions === [];

    if (!$ready || !$apply) {
        roleOutput([
            'result' => $ready ? 'PASS' : 'FAIL',
            'mode' => 'VALIDATE',
            'release' => 'DIAR-6B.5-R2.7.1',
            'readiness' => $readiness,
            'template_count' => count($templates),
            'missing_permissions' => array_keys($missingPermissions),
            'oracle_writes' => 'NONE',
            'next_action' => $ready ? 'Run with --apply to install or update the eight DIAR role templates.' : 'Resolve readiness failures first.',
        ], $ready ? 0 : 50);
    }

    $roleCodes = array_map(static function (array $role): string {
        return (string)$role['code'];
    }, $templates);

    $backup = [
        'release' => 'DIAR-6B.5-R2.7.1',
        'created_at' => date(DATE_ATOM),
        'roles' => [],
        'overrides' => [],
    ];

    foreach ($roleCodes as $roleCode) {
        $row = Database::fetchOne($branch, "SELECT
            ROLE_CODE role_code,
            ROLE_NAME_EN role_name_en,
            ROLE_NAME_AR role_name_ar,
            ROLE_DESCRIPTION_EN role_description_en,
            ROLE_DESCRIPTION_AR role_description_ar,
            POLICY_MODE policy_mode,
            AUTO_INCLUDE_NEW auto_include_new,
            ALL_BRANCHES all_branches,
            IS_SYSTEM_ROLE is_system_role,
            IS_PROTECTED is_protected,
            IS_ACTIVE is_active,
            UPDATED_BY updated_by,
            TO_CHAR(UPDATED_AT,'YYYY-MM-DD HH24:MI:SS') updated_at
        FROM DASH_ACL_ROLES
        WHERE ROLE_CODE=:role_code", ['role_code' => $roleCode]);

        $backup['roles'][$roleCode] = is_array($row) ? $row : null;
        $backup['overrides'][$roleCode] = Database::fetchAll($branch, "SELECT
            PERMISSION_CODE permission_code,
            EFFECT effect,
            UPDATED_BY updated_by,
            TO_CHAR(UPDATED_AT,'YYYY-MM-DD HH24:MI:SS') updated_at
        FROM DASH_ACL_ROLE_OVERRIDES
        WHERE ROLE_CODE=:role_code
        ORDER BY PERMISSION_CODE", ['role_code' => $roleCode]);
    }

    $backupDir = dirname(__DIR__) . '/storage/diar6b-role-backups';
    if (!is_dir($backupDir) && !mkdir($backupDir, 0777, true) && !is_dir($backupDir)) {
        throw new RuntimeException('ROLE_BACKUP_DIRECTORY_CREATE_FAILED');
    }
    $backupPath = $backupDir . '/DIAR_6B_5_R2_7_1_roles_' . date('Ymd_His') . '.json';
    if (file_put_contents($backupPath, json_encode(
        $backup,
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    )) === false) {
        throw new RuntimeException('ROLE_BACKUP_WRITE_FAILED');
    }

    $statements = [];
    foreach ($templates as $role) {
        $statements[] = [
            'sql' => "MERGE INTO DASH_ACL_ROLES t
                USING (SELECT :role_code role_code FROM dual) s
                ON (t.ROLE_CODE=s.role_code)
                WHEN MATCHED THEN UPDATE SET
                    t.ROLE_NAME_EN=:name_en,
                    t.ROLE_NAME_AR=:name_ar,
                    t.ROLE_DESCRIPTION_EN=:description_en,
                    t.ROLE_DESCRIPTION_AR=:description_ar,
                    t.POLICY_MODE='EXPLICIT',
                    t.AUTO_INCLUDE_NEW=0,
                    t.ALL_BRANCHES=:all_branches,
                    t.IS_SYSTEM_ROLE=0,
                    t.IS_PROTECTED=0,
                    t.IS_ACTIVE=1,
                    t.UPDATED_BY=:actor,
                    t.UPDATED_AT=SYSDATE
                WHEN NOT MATCHED THEN INSERT (
                    ROLE_CODE,ROLE_NAME_EN,ROLE_NAME_AR,
                    ROLE_DESCRIPTION_EN,ROLE_DESCRIPTION_AR,
                    POLICY_MODE,AUTO_INCLUDE_NEW,ALL_BRANCHES,
                    IS_SYSTEM_ROLE,IS_PROTECTED,IS_ACTIVE,
                    UPDATED_BY,UPDATED_AT
                ) VALUES (
                    :role_code,:name_en,:name_ar,
                    :description_en,:description_ar,
                    'EXPLICIT',0,:all_branches,
                    0,0,1,:actor,SYSDATE
                )",
            'params' => [
                'role_code' => (string)$role['code'],
                'name_en' => (string)$role['name_en'],
                'name_ar' => (string)$role['name_ar'],
                'description_en' => (string)$role['description_en'],
                'description_ar' => (string)$role['description_ar'],
                'all_branches' => !empty($role['all_branches']) ? 1 : 0,
                'actor' => $actor,
            ],
        ];
        $statements[] = [
            'sql' => "DELETE FROM DASH_ACL_ROLE_OVERRIDES WHERE ROLE_CODE=:role_code",
            'params' => ['role_code' => (string)$role['code']],
        ];
        foreach ((array)$role['permissions'] as $permission) {
            $statements[] = [
                'sql' => "INSERT INTO DASH_ACL_ROLE_OVERRIDES (
                    ROLE_CODE,PERMISSION_CODE,EFFECT,UPDATED_BY,UPDATED_AT
                ) VALUES (
                    :role_code,:permission_code,'ALLOW',:actor,SYSDATE
                )",
                'params' => [
                    'role_code' => (string)$role['code'],
                    'permission_code' => (string)$permission,
                    'actor' => $actor,
                ],
            ];
        }
        $statements[] = [
            'sql' => "INSERT INTO DASH_ACL_AUDIT (
                AUDIT_ID,ACTOR_USERNAME,ACTION_CODE,TARGET_TYPE,TARGET_KEY,
                OLD_VALUE,NEW_VALUE,CREATED_AT
            ) VALUES (
                DASH_ACL_AUDIT_SEQ.NEXTVAL,:actor,'DIAR_ROLE_TEMPLATE_UPSERT',
                'ROLE',:role_code,NULL,:new_value,SYSDATE
            )",
            'params' => [
                'actor' => $actor,
                'role_code' => (string)$role['code'],
                'new_value' => json_encode([
                    'release' => 'DIAR-6B.5-R2.7.1',
                    'permission_count' => count((array)$role['permissions']),
                    'user_assignments' => 'NONE',
                ], JSON_UNESCAPED_SLASHES),
            ],
        ];
    }

    Database::executeTransaction($branch, $statements);

    roleOutput([
        'result' => 'PASS',
        'mode' => 'APPLY',
        'release' => 'DIAR-6B.5-R2.7.1',
        'template_count' => count($templates),
        'backup' => $backupPath,
        'user_assignments' => 'NONE',
        'operational_oracle_writes' => 'NONE',
        'acl_writes' => 'ROLE_TEMPLATES_ONLY',
    ]);
} catch (Throwable $e) {
    roleOutput([
        'result' => 'FAIL',
        'release' => 'DIAR-6B.5-R2.7.1',
        'error' => preg_replace('/\s+/', ' ', $e->getMessage()),
    ], 51);
}
