<?php

declare(strict_types=1);

// Strictly read-only schema/constraint evidence for Department::transfer.
$lane = (string)getenv('CDX_DEPT_SCHEMA_LANE');
if (($argv[1] ?? '') !== '--schema' || !in_array($lane, ['A', 'B', 'C'], true)) {
    fwrite(STDERR, "Department schema probe disabled\n");
    exit(78);
}
$roots = [
    'A' => '/tmp/zhct-http-store-a.l5omH8',
    'B' => '/tmp/zhct-http-store-mysql.dQTTBP',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/store',
];
$root = $roots[$lane];
if ((string)getenv('POC_APP_ROOT') !== $root || is_link($root)) {
    throw new RuntimeException('Approved isolated root mismatch');
}
if (hash_file('sha256', $root . '/application/p/controller/Department.php')
        !== '4c7dc5220e5af83ef3f401bccd4577ab1daccec955de51cb0ce893dd005b9db0'
    || hash_file('sha256', $root . '/application/model/Department.php')
        !== 'a0910fe7209b7da3e6a43a1697ea7a049bbd6e94d2c08225cdefe90f368efce8'
    || hash_file('sha256', $root . '/application/model/StaffDepartment.php')
        !== 'c450c5b72f6e5f1a9f0c0736922b1ccdc1f160b9637970857cf6b794d6becf29'
    || hash_file('sha256', $root . '/application/p/logic/Department.php')
        !== ($lane === 'A'
            ? '2a66074564ce33ddeae2a960cf3bd49128b5261b750dbca6a7d8b467a21d2540'
            : '665e4ba4639c1d27448524228ce3ad4568de1700a578d7dc17d98de197e54c5a')) {
    throw new RuntimeException('Department transfer source drift');
}
$runtime = (static function (string $path): array {
    $config = require $path;
    if (!is_array($config)) throw new RuntimeException('Runtime config invalid');
    return $config;
})($root . '/application/database.php');
$dialect = $lane === 'C' ? 'dm' : 'mysql';
if ($dialect === 'mysql') {
    $local = (static function (): array {
        $config = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
        if (!is_array($config)) throw new RuntimeException('Local DB config invalid');
        return $config;
    })();
    if ((string)($runtime['type'] ?? '') !== 'mysql') {
        throw new RuntimeException('Not code local MySQL');
    }
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if (($runtime[$key] ?? null) !== ($local[$key] ?? null)) {
            throw new RuntimeException('Runtime differs from code local');
        }
    }
    if ((int)($runtime['hostport'] ?? 3306) !== (int)($local['hostport'] ?? 3306)) {
        throw new RuntimeException('Runtime port differs from code local');
    }
    $dsn = 'mysql:host=' . $runtime['hostname'] . ';port='
        . (int)($runtime['hostport'] ?? 3306) . ';dbname=' . $runtime['database']
        . ';charset=' . (string)($runtime['charset'] ?? 'utf8');
} else {
    if ((string)($runtime['type'] ?? '') !== 'app\\cores\\database\\Dm'
        || (string)getenv('POC_HOST') !== '127.0.0.1'
        || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP'
        || strtoupper((string)getenv('POC_USER')) !== 'ZHCT_APP') {
        throw new RuntimeException('DM target mismatch');
    }
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $key => $envKey) {
        if ((string)($runtime[$key] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('DM worker context mismatch');
        }
    }
    $machineId = trim((string)@file_get_contents('/etc/machine-id'));
    if ($machineId === '' || gethostname() === false
        || hash('sha256', gethostname() . '|' . $machineId)
            !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('DM host mismatch');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
}
$pdo = new PDO($dsn, (string)$runtime['username'],
    (string)$runtime['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
$identity = $dialect === 'mysql'
    ? (string)$pdo->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())"
    )->fetchColumn()
    : 'ZHCT_APP|5237|' . (string)$pdo->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL"
    )->fetchColumn();
$approved = $dialect === 'mysql'
    ? '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401'
    : 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194';
if (!hash_equals($approved, hash('sha256', $identity))) {
    throw new RuntimeException('DB identity outside approved target');
}
$read = static function (PDO $connection, string $sql, array $args = []): array {
    $stmt = $connection->prepare($sql);
    $stmt->execute($args);
    $rows = [];
    foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $raw) {
        $row = [];
        foreach (array_change_key_case($raw, CASE_LOWER) as $key => $value) {
            $row[$key] = is_resource($value) ? stream_get_contents($value) : $value;
        }
        $rows[] = $row;
    }
    return $rows;
};
$targetTables = $dialect === 'mysql'
    ? ['ydy_staff_department', 'ydy_department', 'ydy_staff', 'ydy_user']
    : ['YDY_STAFF_DEPARTMENT', 'YDY_DEPARTMENT', 'YDY_STAFF', 'YDY_USER'];
$columns = [];
$indexes = [];
$incoming = [];
$outgoing = [];
$triggers = [];
$fixtureRequired = [];
$mode = null;
if ($dialect === 'mysql') {
    $mode = (string)$pdo->query('SELECT @@sql_mode')->fetchColumn();
    $version = (string)$pdo->query('SELECT VERSION()')->fetchColumn();
    if (strpos($version, '5.6.') !== 0) throw new RuntimeException('Not MySQL 5.6');
    $rows = $read($pdo,
        'SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,EXTRA,'
        . ' CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS'
        . ' WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN (?,?,?,?)'
        . ' ORDER BY TABLE_NAME,ORDINAL_POSITION', $targetTables);
    foreach ($rows as $row) {
        $table = (string)$row['table_name'];
        $name = (string)$row['column_name'];
        if ($row['is_nullable'] === 'NO' && $row['column_default'] === null
            && strpos((string)$row['extra'], 'auto_increment') === false) {
            $fixtureRequired[$table][] = $name;
        }
        if ($table !== 'ydy_staff_department'
            && !in_array($name, ['id', 'uuid', 'puuid', 'leaf_flag', 'name',
                'create_by', 'create_time', 'is_show', 'status',
                'account', 'staff_uuid', 'type'], true)
            && !in_array($name, $fixtureRequired[$table] ?? [], true)) continue;
        $columns[$table][$name] = ['type' => $row['data_type'],
            'nullable' => $row['is_nullable'],
            'default' => $row['column_default'] === null ? '<NULL>'
                : ($row['column_default'] === '' ? '<EMPTY_STRING>'
                    : (string)$row['column_default']),
            'extra' => (string)$row['extra'],
            'max_chars' => $row['character_maximum_length'] === null ? null
                : (int)$row['character_maximum_length']];
    }
    $indexRows = $read($pdo,
        'SELECT TABLE_NAME,INDEX_NAME,NON_UNIQUE,SEQ_IN_INDEX,COLUMN_NAME'
        . ' FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE()'
        . ' AND TABLE_NAME IN (?,?) ORDER BY TABLE_NAME,INDEX_NAME,SEQ_IN_INDEX',
        array_slice($targetTables, 0, 2));
    foreach ($indexRows as $row) {
        $key = (string)$row['table_name'] . '.' . (string)$row['index_name'];
        if (!isset($indexes[$key])) $indexes[$key] = [
            'unique' => (int)$row['non_unique'] === 0, 'columns' => []];
        $indexes[$key]['columns'][] = (string)$row['column_name'];
    }
    $incoming = $read($pdo,
        'SELECT REFERENCED_TABLE_NAME,TABLE_NAME,CONSTRAINT_NAME,COLUMN_NAME'
        . ' FROM information_schema.KEY_COLUMN_USAGE'
        . ' WHERE REFERENCED_TABLE_SCHEMA=DATABASE()'
        . ' AND REFERENCED_TABLE_NAME IN (?,?) ORDER BY REFERENCED_TABLE_NAME,TABLE_NAME',
        array_slice($targetTables, 0, 2));
    $outgoing = $read($pdo,
        'SELECT TABLE_NAME,CONSTRAINT_NAME,REFERENCED_TABLE_NAME,COLUMN_NAME'
        . ' FROM information_schema.KEY_COLUMN_USAGE'
        . ' WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN (?,?)'
        . ' AND REFERENCED_TABLE_NAME IS NOT NULL ORDER BY TABLE_NAME,CONSTRAINT_NAME',
        array_slice($targetTables, 0, 2));
    $triggers = $read($pdo,
        'SELECT EVENT_OBJECT_TABLE,TRIGGER_NAME,ACTION_TIMING,EVENT_MANIPULATION'
        . ' FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE()'
        . ' AND EVENT_OBJECT_TABLE IN (?,?) ORDER BY EVENT_OBJECT_TABLE,TRIGGER_NAME',
        array_slice($targetTables, 0, 2));
} else {
    $rows = $read($pdo,
        "SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT,DATA_LENGTH"
        . " FROM USER_TAB_COLUMNS WHERE TABLE_NAME IN ('YDY_STAFF_DEPARTMENT',"
        . "'YDY_DEPARTMENT','YDY_STAFF','YDY_USER')"
        . ' ORDER BY TABLE_NAME,COLUMN_ID');
    foreach ($rows as $row) {
        $table = (string)$row['table_name'];
        $name = (string)$row['column_name'];
        if ($row['nullable'] === 'N' && $row['data_default'] === null
            && $name !== 'ID') {
            $fixtureRequired[$table][] = $name;
        }
        if ($table !== 'YDY_STAFF_DEPARTMENT'
            && !in_array($name, ['ID', 'UUID', 'PUUID', 'LEAF_FLAG', 'NAME',
                'CREATE_BY', 'CREATE_TIME', 'IS_SHOW', 'STATUS',
                'ACCOUNT', 'STAFF_UUID', 'TYPE'], true)
            && !in_array($name, $fixtureRequired[$table] ?? [], true)) continue;
        $columns[$table][$name] = ['type' => (string)$row['data_type'],
            'nullable' => (string)$row['nullable'],
            'default' => $row['data_default'] === null ? '<NULL>'
                : (string)$row['data_default'],
            'data_length' => (int)$row['data_length']];
    }
    $indexRows = $read($pdo,
        "SELECT i.TABLE_NAME,i.INDEX_NAME,i.UNIQUENESS,c.COLUMN_NAME,c.COLUMN_POSITION"
        . ' FROM USER_INDEXES i JOIN USER_IND_COLUMNS c'
        . ' ON c.INDEX_NAME=i.INDEX_NAME AND c.TABLE_NAME=i.TABLE_NAME'
        . " WHERE i.TABLE_NAME IN ('YDY_STAFF_DEPARTMENT','YDY_DEPARTMENT')"
        . ' ORDER BY i.TABLE_NAME,i.INDEX_NAME,c.COLUMN_POSITION');
    foreach ($indexRows as $row) {
        $key = (string)$row['table_name'] . '.' . (string)$row['index_name'];
        if (!isset($indexes[$key])) $indexes[$key] = [
            'unique' => strtoupper((string)$row['uniqueness']) === 'UNIQUE',
            'columns' => []];
        $indexes[$key]['columns'][] = (string)$row['column_name'];
    }
    $incoming = $read($pdo,
        "SELECT p.TABLE_NAME AS REFERENCED_TABLE_NAME,c.TABLE_NAME,c.CONSTRAINT_NAME"
        . ' FROM ALL_CONSTRAINTS c JOIN ALL_CONSTRAINTS p'
        . ' ON c.R_OWNER=p.OWNER AND c.R_CONSTRAINT_NAME=p.CONSTRAINT_NAME'
        . " WHERE c.CONSTRAINT_TYPE='R' AND p.OWNER='ZHCT_APP'"
        . " AND p.TABLE_NAME IN ('YDY_STAFF_DEPARTMENT','YDY_DEPARTMENT')"
        . ' ORDER BY p.TABLE_NAME,c.TABLE_NAME,c.CONSTRAINT_NAME');
    $outgoing = $read($pdo,
        "SELECT TABLE_NAME,CONSTRAINT_NAME,R_CONSTRAINT_NAME FROM USER_CONSTRAINTS"
        . " WHERE CONSTRAINT_TYPE='R'"
        . " AND TABLE_NAME IN ('YDY_STAFF_DEPARTMENT','YDY_DEPARTMENT')"
        . ' ORDER BY TABLE_NAME,CONSTRAINT_NAME');
    $triggers = $read($pdo,
        "SELECT TABLE_NAME,TRIGGER_NAME,TRIGGERING_EVENT,STATUS FROM USER_TRIGGERS"
        . " WHERE TABLE_NAME IN ('YDY_STAFF_DEPARTMENT','YDY_DEPARTMENT')"
        . ' ORDER BY TABLE_NAME,TRIGGER_NAME');
}
$result = ['lane' => $lane, 'dialect' => $dialect,
    'database_identity_approved' => true,
    'sql_mode' => $mode,
    'columns' => $columns,
    'indexes' => $indexes,
    'incoming_fk' => $incoming,
    'outgoing_fk' => $outgoing,
    'triggers' => $triggers,
    'fixture_required_no_default_excluding_identity' => $fixtureRequired,
    'business_http_executed' => false,
    'dml_executed' => false];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
