import { describe, it, expect } from 'vitest';
import { readFileSync } from 'fs';
import { resolve } from 'path';

/**
 * Regression guard for issue #4853: fresh install black-screens on Android
 * devices whose external storage volume can't be prepared (e.g. custom ROMs
 * where `/storage/emulated/0/Android/data/<pkg>/cache` fails to mkdir).
 *
 * Root cause was in this capability file. The fs plugin's built-in permission
 * sets `fs:allow-cache-read` / `fs:allow-cache-write` expand to
 * `read-all`/`write-all` + `scope-cache`, and `scope-cache` carries the
 * EXTERNAL cache base dir (`$CACHE` -> Android `getExternalCacheDir`). An inline
 * `allow` of the `$APPCACHE` glob on those grants only ADDS scope; it cannot
 * strip the set's built-in `$CACHE`. At startup Tauri resolves every scope entry
 * (`ScopeObject::deserialize` -> `app.path().parse("$CACHE")` ->
 * `getExternalCacheDir`). On the broken device that returns null, the resolve
 * errors, and the graceful "skip unresolvable entry" arm is
 * `#[cfg(not(target_os = "android"))]` — disabled on Android — so the error
 * propagates, fs capability/scope build fails, app init aborts -> black screen.
 *
 * Readest only ever does I/O under the INTERNAL app cache (`$APPCACHE` ->
 * `getCacheDir`, always available). It must never pull in the external `$CACHE`
 * scope. The fix grants the raw command sets (`fs:read-all`/`fs:write-all`) plus
 * an `$APPCACHE` scope entry instead of the cache permission bundles.
 *
 * Invariant: the `default` capability must not grant any fs permission that
 * carries the external `$CACHE` base directory, while still scoping the internal
 * `$APPCACHE` and keeping full read/write command coverage.
 */

// fs plugin permission identifiers whose scope resolves the EXTERNAL `$CACHE`
// (see packages/tauri-plugins/plugins/fs/permissions/autogenerated/
//  base-directories/cache.toml). Granting any of these resolves
// `getExternalCacheDir` at startup.
const EXTERNAL_CACHE_FS_PERMISSIONS = [
  'fs:scope-cache',
  'fs:scope-cache-index',
  'fs:scope-cache-recursive',
  'fs:allow-cache-read',
  'fs:allow-cache-write',
  'fs:allow-cache-read-recursive',
  'fs:allow-cache-write-recursive',
  'fs:allow-cache-meta',
  'fs:allow-cache-meta-recursive',
];

type PermissionEntry = string | { identifier: string; allow?: { path?: string }[] };

const capability = JSON.parse(
  readFileSync(resolve(process.cwd(), 'src-tauri/capabilities/default.json'), 'utf-8'),
) as { permissions: PermissionEntry[] };

const identifierOf = (entry: PermissionEntry): string =>
  typeof entry === 'string' ? entry : entry.identifier;

const grantedIdentifiers = capability.permissions.map(identifierOf);

const scopePaths = capability.permissions.flatMap((entry) =>
  typeof entry === 'string' ? [] : (entry.allow ?? []).map((a) => a.path).filter(Boolean),
);

describe('default capability fs cache scope (#4853)', () => {
  it('does not grant any external-$CACHE fs permission', () => {
    const offenders = grantedIdentifiers.filter((id) => EXTERNAL_CACHE_FS_PERMISSIONS.includes(id));
    expect(offenders).toEqual([]);
  });

  it('still scopes the internal app cache ($APPCACHE)', () => {
    expect(scopePaths).toContain('$APPCACHE/**/*');
  });

  it('keeps full fs read/write command coverage via the scope-free command sets', () => {
    expect(grantedIdentifiers).toContain('fs:read-all');
    expect(grantedIdentifiers).toContain('fs:write-all');
  });
});
